mirror of
https://github.com/bombsquad-community/plugin-manager.git
synced 2025-10-08 14:54:36 +00:00
2755 lines
120 KiB
Python
2755 lines
120 KiB
Python
__author__ = 'Droopy'
|
|
__version__ = 4.0
|
|
|
|
# ba_meta require api 7
|
|
import datetime
|
|
import json
|
|
import math
|
|
import os
|
|
import pickle
|
|
import random
|
|
import time
|
|
import urllib.request
|
|
import weakref
|
|
from threading import Thread
|
|
from typing import List, Tuple, Sequence, Optional, Dict, Any, cast
|
|
from hashlib import md5
|
|
|
|
import _ba
|
|
import ba
|
|
import bastd.ui.party
|
|
from bastd.ui.colorpicker import ColorPickerExact
|
|
from bastd.ui.confirm import ConfirmWindow
|
|
from bastd.ui.mainmenu import MainMenuWindow
|
|
from bastd.ui.popup import PopupMenuWindow, PopupWindow, PopupMenu
|
|
|
|
_ip = '127.0.0.1'
|
|
_port = 43210
|
|
_ping = '-'
|
|
url = 'http://bombsquadprivatechat.ml'
|
|
last_msg = None
|
|
|
|
my_directory = _ba.env()['python_directory_user'] + '/UltraPartyWindowFiles/'
|
|
quick_msg_file = my_directory + 'QuickMessages.txt'
|
|
cookies_file = my_directory + 'cookies.txt'
|
|
saved_ids_file = my_directory + 'saved_ids.json'
|
|
my_location = my_directory
|
|
|
|
|
|
def initialize():
|
|
config_defaults = {'Party Chat Muted': False,
|
|
'Chat Muted': False,
|
|
'ping button': True,
|
|
'IP button': True,
|
|
'copy button': True,
|
|
'Direct Send': False,
|
|
'Colorful Chat': True,
|
|
'Custom Commands': [],
|
|
'Message Notification': 'bottom',
|
|
'Self Status': 'online',
|
|
'Translate Source Language': '',
|
|
'Translate Destination Language': 'en',
|
|
'Pronunciation': True
|
|
}
|
|
config = ba.app.config
|
|
for key in config_defaults:
|
|
if key not in config:
|
|
config[key] = config_defaults[key]
|
|
|
|
if not os.path.exists(my_directory):
|
|
os.makedirs(my_directory)
|
|
if not os.path.exists(cookies_file):
|
|
with open(cookies_file, 'wb') as f:
|
|
pickle.dump({}, f)
|
|
if not os.path.exists(saved_ids_file):
|
|
with open(saved_ids_file, 'w') as f:
|
|
data = {}
|
|
json.dump(data, f)
|
|
|
|
|
|
def display_error(msg=None):
|
|
if msg:
|
|
ba.screenmessage(msg, (1, 0, 0))
|
|
else:
|
|
ba.screenmessage('Failed!', (1, 0, 0))
|
|
ba.playsound(ba.getsound('error'))
|
|
|
|
|
|
def display_success(msg=None):
|
|
if msg:
|
|
ba.screenmessage(msg, (0, 1, 0))
|
|
else:
|
|
ba.screenmessage('Successful!', (0, 1, 0))
|
|
|
|
|
|
class Translate(Thread):
|
|
def __init__(self, data, callback):
|
|
super().__init__()
|
|
self.data = data
|
|
self._callback = callback
|
|
|
|
def run(self):
|
|
_ba.pushcall(ba.Call(ba.screenmessage, 'Translating...'), from_other_thread=True)
|
|
response = messenger._send_request(f'{url}/translate', self.data)
|
|
if response:
|
|
_ba.pushcall(ba.Call(self._callback, response), from_other_thread=True)
|
|
|
|
|
|
class ColorTracker:
|
|
def __init__(self):
|
|
self.saved = {}
|
|
|
|
def _get_safe_color(self, sender):
|
|
while True:
|
|
color = (random.random(), random.random(), random.random())
|
|
s = 0
|
|
background = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
for i, j in zip(color, background):
|
|
s += (i - j) ** 2
|
|
if s > 0.1:
|
|
self.saved[sender] = color
|
|
if len(self.saved) > 20:
|
|
self.saved.pop(list(self.saved.keys())[0])
|
|
break
|
|
time.sleep(0.1)
|
|
|
|
def _get_sender_color(self, sender):
|
|
if sender not in self.saved:
|
|
self.thread = Thread(target=self._get_safe_color, args=(sender,))
|
|
self.thread.start()
|
|
return (1, 1, 1)
|
|
else:
|
|
return self.saved[sender]
|
|
|
|
|
|
class PrivateChatHandler:
|
|
def __init__(self):
|
|
self.pvt_msgs = {}
|
|
self.login_id = None
|
|
self.last_msg_id = None
|
|
self.logged_in = False
|
|
self.cookieProcessor = urllib.request.HTTPCookieProcessor()
|
|
self.opener = urllib.request.build_opener(self.cookieProcessor)
|
|
self.filter = 'all'
|
|
self.pending_messages = []
|
|
self.friends_status = {}
|
|
self.error = ''
|
|
Thread(target=self._ping).start()
|
|
|
|
def _load_ids(self):
|
|
with open(saved_ids_file, 'r') as f:
|
|
saved = json.load(f)
|
|
if self.myid in saved:
|
|
self.saved_ids = saved[self.myid]
|
|
else:
|
|
self.saved_ids = {'all': '<all>'}
|
|
|
|
def _dump_ids(self):
|
|
with open(saved_ids_file, 'r') as f:
|
|
saved = json.load(f)
|
|
with open(saved_ids_file, 'w') as f:
|
|
saved[self.myid] = self.saved_ids
|
|
json.dump(saved, f)
|
|
|
|
def _ping(self):
|
|
self.server_online = False
|
|
response = self._send_request(url=f'{url}')
|
|
if not response:
|
|
self.error = 'Server offline'
|
|
elif response:
|
|
try:
|
|
self.server_online = True
|
|
version = float(response.replace('v', ''))
|
|
except:
|
|
self.error = 'Server offline'
|
|
|
|
def _signup(self, registration_key):
|
|
data = dict(pb_id=self.myid, registration_key=registration_key)
|
|
response = self._send_request(url=f'{url}/signup', data=data)
|
|
if response:
|
|
if response == 'successful':
|
|
display_success('Account Created Successfully')
|
|
self._login(registration_key=registration_key)
|
|
return True
|
|
display_error(response)
|
|
|
|
def _save_cookie(self):
|
|
with open(cookies_file, 'rb') as f:
|
|
cookies = pickle.load(f)
|
|
with open(cookies_file, 'wb') as f:
|
|
for c in self.cookieProcessor.cookiejar:
|
|
cookie = pickle.dumps(c)
|
|
break
|
|
cookies[self.myid] = cookie
|
|
pickle.dump(cookies, f)
|
|
|
|
def _cookie_login(self):
|
|
self.myid = ba.internal.get_v1_account_misc_read_val_2('resolvedAccountID', '')
|
|
try:
|
|
with open(cookies_file, 'rb') as f:
|
|
cookies = pickle.load(f)
|
|
except:
|
|
return False
|
|
if self.myid in cookies:
|
|
cookie = pickle.loads(cookies[self.myid])
|
|
self.cookieProcessor.cookiejar.set_cookie(cookie)
|
|
self.opener = urllib.request.build_opener(self.cookieProcessor)
|
|
response = self._send_request(url=f'{url}/login')
|
|
if response.startswith('logged in as'):
|
|
self.logged_in = True
|
|
self._load_ids()
|
|
display_success(response)
|
|
return True
|
|
|
|
def _login(self, registration_key):
|
|
self.myid = ba.internal.get_v1_account_misc_read_val_2('resolvedAccountID', '')
|
|
data = dict(pb_id=self.myid, registration_key=registration_key)
|
|
response = self._send_request(url=f'{url}/login', data=data)
|
|
if response == 'successful':
|
|
self.logged_in = True
|
|
self._load_ids()
|
|
self._save_cookie()
|
|
display_success('Account Logged in Successfully')
|
|
return True
|
|
else:
|
|
display_error(response)
|
|
|
|
def _query(self, pb_id=None):
|
|
if not pb_id:
|
|
pb_id = self.myid
|
|
response = self._send_request(url=f'{url}/query/{pb_id}')
|
|
if response == 'exists':
|
|
return True
|
|
return False
|
|
|
|
def _send_request(self, url, data=None):
|
|
try:
|
|
if not data:
|
|
response = self.opener.open(url)
|
|
else:
|
|
response = self.opener.open(url, data=json.dumps(data).encode())
|
|
if response.getcode() != 200:
|
|
display_error(response.read().decode())
|
|
return None
|
|
else:
|
|
return response.read().decode()
|
|
except:
|
|
return None
|
|
|
|
def _save_id(self, account_id, nickname='<default>', verify=True):
|
|
# display_success(f'Saving {account_id}. Please wait...')
|
|
if verify:
|
|
url = 'http://bombsquadgame.com/accountquery?id=' + account_id
|
|
response = json.loads(urllib.request.urlopen(url).read().decode())
|
|
if 'error' in response:
|
|
display_error('Enter valid account id')
|
|
return False
|
|
self.saved_ids[account_id] = {}
|
|
name = None
|
|
if nickname == '<default>':
|
|
name_html = response['name_html']
|
|
name = name_html.split('>')[1]
|
|
nick = name if name else nickname
|
|
else:
|
|
nick = nickname
|
|
self.saved_ids[account_id] = nick
|
|
self._dump_ids()
|
|
display_success(f'Account added: {nick}({account_id})')
|
|
return True
|
|
|
|
def _remove_id(self, account_id):
|
|
removed = self.saved_ids.pop(account_id)
|
|
self._dump_ids()
|
|
ba.screenmessage(f'Removed successfully: {removed}({account_id})', (0, 1, 0))
|
|
ba.playsound(ba.getsound('shieldDown'))
|
|
|
|
def _format_message(self, msg):
|
|
filter = msg['filter']
|
|
if filter in self.saved_ids:
|
|
if self.filter == 'all':
|
|
message = '[' + self.saved_ids[filter] + ']' + msg['message']
|
|
else:
|
|
message = msg['message']
|
|
else:
|
|
message = '[' + msg['filter'] + ']: ' + \
|
|
'Message from unsaved id. Save id to view message.'
|
|
return message
|
|
|
|
def _get_status(self, id, type='status'):
|
|
info = self.friends_status.get(id, {})
|
|
if not info:
|
|
return '-'
|
|
if type == 'status':
|
|
return info['status']
|
|
else:
|
|
last_seen = info["last_seen"]
|
|
last_seen = _get_local_time(last_seen)
|
|
ba.screenmessage(f'Last seen on: {last_seen}')
|
|
|
|
|
|
def _get_local_time(utctime):
|
|
d = datetime.datetime.strptime(utctime, '%d-%m-%Y %H:%M:%S')
|
|
d = d.replace(tzinfo=datetime.timezone.utc)
|
|
d = d.astimezone()
|
|
return d.strftime('%B %d,\t\t%H:%M:%S')
|
|
|
|
|
|
def update_status():
|
|
if messenger.logged_in:
|
|
if ba.app.config['Self Status'] == 'online':
|
|
host = _ba.get_connection_to_host_info().get('name', '')
|
|
if host:
|
|
my_status = f'Playing in {host}'
|
|
else:
|
|
my_status = 'in Lobby'
|
|
ids_to_check = [i for i in messenger.saved_ids if i != 'all']
|
|
response = messenger._send_request(url=f'{url}/updatestatus',
|
|
data=dict(self_status=my_status, ids=ids_to_check))
|
|
if response:
|
|
messenger.friends_status = json.loads(response)
|
|
else:
|
|
messenger.friends_status = {}
|
|
|
|
|
|
def messenger_thread():
|
|
counter = 0
|
|
while True:
|
|
counter += 1
|
|
time.sleep(0.6)
|
|
check_new_message()
|
|
if counter > 5:
|
|
counter = 0
|
|
update_status()
|
|
|
|
|
|
def check_new_message():
|
|
if messenger.logged_in:
|
|
if messenger.login_id != messenger.myid:
|
|
response = messenger._send_request(f'{url}/first')
|
|
if response:
|
|
messenger.pvt_msgs = json.loads(response)
|
|
if messenger.pvt_msgs['all']:
|
|
messenger.last_msg_id = messenger.pvt_msgs['all'][-1]['id']
|
|
messenger.login_id = messenger.myid
|
|
else:
|
|
response = messenger._send_request(f'{url}/new/{messenger.last_msg_id}')
|
|
if response:
|
|
new_msgs = json.loads(response)
|
|
if new_msgs:
|
|
for msg in new_msgs['messages']:
|
|
if msg['id'] > messenger.last_msg_id:
|
|
messenger.last_msg_id = msg['id']
|
|
messenger.pvt_msgs['all'].append(
|
|
dict(id=msg['id'], filter=msg['filter'], message=msg['message'], sent=msg['sent']))
|
|
if len(messenger.pvt_msgs['all']) > 40:
|
|
messenger.pvt_msgs['all'].pop(0)
|
|
if msg['filter'] not in messenger.pvt_msgs:
|
|
messenger.pvt_msgs[msg['filter']] = [
|
|
dict(id=msg['id'], filter=msg['filter'], message=msg['message'], sent=msg['sent'])]
|
|
else:
|
|
messenger.pvt_msgs[msg['filter']].append(
|
|
dict(id=msg['id'], filter=msg['filter'], message=msg['message'], sent=msg['sent']))
|
|
if len(messenger.pvt_msgs[msg['filter']]) > 20:
|
|
messenger.pvt_msgs[msg['filter']].pop(0)
|
|
messenger.pending_messages.append(
|
|
(messenger._format_message(msg), msg['filter'], msg['sent']))
|
|
|
|
|
|
def display_message(msg, msg_type, filter=None, sent=None):
|
|
flag = None
|
|
notification = ba.app.config['Message Notification']
|
|
if _ba.app.ui.party_window:
|
|
if _ba.app.ui.party_window():
|
|
if _ba.app.ui.party_window()._private_chat:
|
|
flag = 1
|
|
if msg_type == 'private':
|
|
if messenger.filter == filter or messenger.filter == 'all':
|
|
_ba.app.ui.party_window().on_chat_message(msg, sent)
|
|
else:
|
|
if notification == 'top':
|
|
ba.screenmessage(msg, (1, 1, 0), True, ba.gettexture('coin'))
|
|
else:
|
|
ba.screenmessage(msg, (1, 1, 0), False)
|
|
else:
|
|
ba.screenmessage(msg, (0.2, 1.0, 1.0), True, ba.gettexture('circleShadow'))
|
|
else:
|
|
flag = 1
|
|
if msg_type == 'private':
|
|
if notification == 'top':
|
|
ba.screenmessage(msg, (1, 1, 0), True, ba.gettexture('coin'))
|
|
else:
|
|
ba.screenmessage(msg, (1, 1, 0), False)
|
|
if not flag:
|
|
if msg_type == 'private':
|
|
if notification == 'top':
|
|
ba.screenmessage(msg, (1, 1, 0), True, ba.gettexture('coin'))
|
|
else:
|
|
ba.screenmessage(msg, (1, 1, 0), False)
|
|
else:
|
|
ba.screenmessage(msg, (0.2, 1.0, 1.0), True, ba.gettexture('circleShadow'))
|
|
|
|
|
|
def msg_displayer():
|
|
for msg in messenger.pending_messages:
|
|
display_message(msg[0], 'private', msg[1], msg[2])
|
|
messenger.pending_messages.remove(msg)
|
|
if ba.app.config['Chat Muted'] and not ba.app.config['Party Chat Muted']:
|
|
global last_msg
|
|
last = _ba.get_chat_messages()
|
|
lm = last[-1] if last else None
|
|
if lm != last_msg:
|
|
last_msg = lm
|
|
display_message(lm, 'public')
|
|
|
|
|
|
class SortQuickMessages:
|
|
def __init__(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
bg_color = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
self._width = 750 if uiscale is ba.UIScale.SMALL else 600
|
|
self._height = (300 if uiscale is ba.UIScale.SMALL else
|
|
325 if uiscale is ba.UIScale.MEDIUM else 350)
|
|
self._root_widget = ba.containerwidget(
|
|
size=(self._width, self._height),
|
|
transition='in_right',
|
|
on_outside_click_call=self._save,
|
|
color=bg_color,
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
scale=(2.0 if uiscale is ba.UIScale.SMALL else
|
|
1.3 if uiscale is ba.UIScale.MEDIUM else 1.0),
|
|
stack_offset=(0, -16) if uiscale is ba.UIScale.SMALL else (0, 0))
|
|
ba.textwidget(parent=self._root_widget,
|
|
position=(-10, self._height - 50),
|
|
size=(self._width, 25),
|
|
text='Sort Quick Messages',
|
|
color=ba.app.ui.title_color,
|
|
scale=1.05,
|
|
h_align='center',
|
|
v_align='center',
|
|
maxwidth=270)
|
|
b_textcolor = (0.4, 0.75, 0.5)
|
|
up_button = ba.buttonwidget(parent=self._root_widget,
|
|
position=(10, 170),
|
|
size=(75, 75),
|
|
on_activate_call=self._move_up,
|
|
label=ba.charstr(ba.SpecialChar.UP_ARROW),
|
|
button_type='square',
|
|
color=bg_color,
|
|
textcolor=b_textcolor,
|
|
autoselect=True,
|
|
repeat=True)
|
|
down_button = ba.buttonwidget(parent=self._root_widget,
|
|
position=(10, 75),
|
|
size=(75, 75),
|
|
on_activate_call=self._move_down,
|
|
label=ba.charstr(ba.SpecialChar.DOWN_ARROW),
|
|
button_type='square',
|
|
color=bg_color,
|
|
textcolor=b_textcolor,
|
|
autoselect=True,
|
|
repeat=True)
|
|
self._scroll_width = self._width - 150
|
|
self._scroll_height = self._height - 110
|
|
self._scrollwidget = ba.scrollwidget(
|
|
parent=self._root_widget,
|
|
size=(self._scroll_width, self._scroll_height),
|
|
color=bg_color,
|
|
position=(100, 40))
|
|
self._columnwidget = ba.columnwidget(
|
|
parent=self._scrollwidget,
|
|
border=2,
|
|
margin=0)
|
|
with open(quick_msg_file, 'r') as f:
|
|
self.msgs = f.read().split('\n')
|
|
self._msg_selected = None
|
|
self._refresh()
|
|
ba.containerwidget(edit=self._root_widget,
|
|
on_cancel_call=self._save)
|
|
|
|
def _refresh(self):
|
|
for child in self._columnwidget.get_children():
|
|
child.delete()
|
|
for msg in enumerate(self.msgs):
|
|
txt = ba.textwidget(
|
|
parent=self._columnwidget,
|
|
size=(self._scroll_width - 10, 30),
|
|
selectable=True,
|
|
always_highlight=True,
|
|
on_select_call=ba.Call(self._on_msg_select, msg),
|
|
text=msg[1],
|
|
h_align='left',
|
|
v_align='center',
|
|
maxwidth=self._scroll_width)
|
|
if msg == self._msg_selected:
|
|
ba.columnwidget(edit=self._columnwidget,
|
|
selected_child=txt,
|
|
visible_child=txt)
|
|
|
|
def _on_msg_select(self, msg):
|
|
self._msg_selected = msg
|
|
|
|
def _move_up(self):
|
|
index = self._msg_selected[0]
|
|
msg = self._msg_selected[1]
|
|
if index:
|
|
self.msgs.insert((index - 1), self.msgs.pop(index))
|
|
self._msg_selected = (index - 1, msg)
|
|
self._refresh()
|
|
|
|
def _move_down(self):
|
|
index = self._msg_selected[0]
|
|
msg = self._msg_selected[1]
|
|
if index + 1 < len(self.msgs):
|
|
self.msgs.insert((index + 1), self.msgs.pop(index))
|
|
self._msg_selected = (index + 1, msg)
|
|
self._refresh()
|
|
|
|
def _save(self) -> None:
|
|
try:
|
|
with open(quick_msg_file, 'w') as f:
|
|
f.write('\n'.join(self.msgs))
|
|
except:
|
|
ba.print_exception()
|
|
ba.screenmessage('Error!', (1, 0, 0))
|
|
ba.containerwidget(
|
|
edit=self._root_widget,
|
|
transition='out_right')
|
|
|
|
|
|
class TranslationSettings:
|
|
def __init__(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
height = (300 if uiscale is ba.UIScale.SMALL else
|
|
350 if uiscale is ba.UIScale.MEDIUM else 400)
|
|
width = (500 if uiscale is ba.UIScale.SMALL else
|
|
600 if uiscale is ba.UIScale.MEDIUM else 650)
|
|
self._transition_out: Optional[str]
|
|
scale_origin: Optional[Tuple[float, float]]
|
|
self._transition_out = 'out_scale'
|
|
scale_origin = 10
|
|
transition = 'in_scale'
|
|
scale_origin = None
|
|
cancel_is_selected = False
|
|
cfg = ba.app.config
|
|
bg_color = cfg.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
|
|
LANGUAGES = {
|
|
'': 'Auto-Detect',
|
|
'af': 'afrikaans',
|
|
'sq': 'albanian',
|
|
'am': 'amharic',
|
|
'ar': 'arabic',
|
|
'hy': 'armenian',
|
|
'az': 'azerbaijani',
|
|
'eu': 'basque',
|
|
'be': 'belarusian',
|
|
'bn': 'bengali',
|
|
'bs': 'bosnian',
|
|
'bg': 'bulgarian',
|
|
'ca': 'catalan',
|
|
'ceb': 'cebuano',
|
|
'ny': 'chichewa',
|
|
'zh-cn': 'chinese (simplified)',
|
|
'zh-tw': 'chinese (traditional)',
|
|
'co': 'corsican',
|
|
'hr': 'croatian',
|
|
'cs': 'czech',
|
|
'da': 'danish',
|
|
'nl': 'dutch',
|
|
'en': 'english',
|
|
'eo': 'esperanto',
|
|
'et': 'estonian',
|
|
'tl': 'filipino',
|
|
'fi': 'finnish',
|
|
'fr': 'french',
|
|
'fy': 'frisian',
|
|
'gl': 'galician',
|
|
'ka': 'georgian',
|
|
'de': 'german',
|
|
'el': 'greek',
|
|
'gu': 'gujarati',
|
|
'ht': 'haitian creole',
|
|
'ha': 'hausa',
|
|
'haw': 'hawaiian',
|
|
'iw': 'hebrew',
|
|
'he': 'hebrew',
|
|
'hi': 'hindi',
|
|
'hmn': 'hmong',
|
|
'hu': 'hungarian',
|
|
'is': 'icelandic',
|
|
'ig': 'igbo',
|
|
'id': 'indonesian',
|
|
'ga': 'irish',
|
|
'it': 'italian',
|
|
'ja': 'japanese',
|
|
'jw': 'javanese',
|
|
'kn': 'kannada',
|
|
'kk': 'kazakh',
|
|
'km': 'khmer',
|
|
'ko': 'korean',
|
|
'ku': 'kurdish (kurmanji)',
|
|
'ky': 'kyrgyz',
|
|
'lo': 'lao',
|
|
'la': 'latin',
|
|
'lv': 'latvian',
|
|
'lt': 'lithuanian',
|
|
'lb': 'luxembourgish',
|
|
'mk': 'macedonian',
|
|
'mg': 'malagasy',
|
|
'ms': 'malay',
|
|
'ml': 'malayalam',
|
|
'mt': 'maltese',
|
|
'mi': 'maori',
|
|
'mr': 'marathi',
|
|
'mn': 'mongolian',
|
|
'my': 'myanmar (burmese)',
|
|
'ne': 'nepali',
|
|
'no': 'norwegian',
|
|
'or': 'odia',
|
|
'ps': 'pashto',
|
|
'fa': 'persian',
|
|
'pl': 'polish',
|
|
'pt': 'portuguese',
|
|
'pa': 'punjabi',
|
|
'ro': 'romanian',
|
|
'ru': 'russian',
|
|
'sm': 'samoan',
|
|
'gd': 'scots gaelic',
|
|
'sr': 'serbian',
|
|
'st': 'sesotho',
|
|
'sn': 'shona',
|
|
'sd': 'sindhi',
|
|
'si': 'sinhala',
|
|
'sk': 'slovak',
|
|
'sl': 'slovenian',
|
|
'so': 'somali',
|
|
'es': 'spanish',
|
|
'su': 'sundanese',
|
|
'sw': 'swahili',
|
|
'sv': 'swedish',
|
|
'tg': 'tajik',
|
|
'ta': 'tamil',
|
|
'te': 'telugu',
|
|
'th': 'thai',
|
|
'tr': 'turkish',
|
|
'uk': 'ukrainian',
|
|
'ur': 'urdu',
|
|
'ug': 'uyghur',
|
|
'uz': 'uzbek',
|
|
'vi': 'vietnamese',
|
|
'cy': 'welsh',
|
|
'xh': 'xhosa',
|
|
'yi': 'yiddish',
|
|
'yo': 'yoruba',
|
|
'zu': 'zulu'}
|
|
|
|
self.root_widget = ba.containerwidget(
|
|
size=(width, height),
|
|
color=bg_color,
|
|
transition=transition,
|
|
toolbar_visibility='menu_minimal_no_back',
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
on_outside_click_call=self._cancel,
|
|
scale=(2.1 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0),
|
|
scale_origin_stack_offset=scale_origin)
|
|
ba.textwidget(parent=self.root_widget,
|
|
position=(width * 0.5, height - 45),
|
|
size=(20, 20),
|
|
h_align='center',
|
|
v_align='center',
|
|
text="Text Translation",
|
|
scale=0.9,
|
|
color=(5, 5, 5))
|
|
cbtn = btn = ba.buttonwidget(parent=self.root_widget,
|
|
autoselect=True,
|
|
position=(30, height - 60),
|
|
size=(30, 30),
|
|
label=ba.charstr(ba.SpecialChar.BACK),
|
|
button_type='backSmall',
|
|
on_activate_call=self._cancel)
|
|
|
|
source_lang_text = ba.textwidget(parent=self.root_widget,
|
|
position=(40, height - 110),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text="Source Language : ",
|
|
scale=0.9,
|
|
color=(1, 1, 1))
|
|
|
|
source_lang_menu = PopupMenu(
|
|
parent=self.root_widget,
|
|
position=(330 if uiscale is ba.UIScale.SMALL else 400, height - 115),
|
|
width=200,
|
|
scale=(2.8 if uiscale is ba.UIScale.SMALL else
|
|
1.8 if uiscale is ba.UIScale.MEDIUM else 1.2),
|
|
current_choice=cfg['Translate Source Language'],
|
|
choices=LANGUAGES.keys(),
|
|
choices_display=(ba.Lstr(value=i) for i in LANGUAGES.values()),
|
|
button_size=(130, 35),
|
|
on_value_change_call=self._change_source)
|
|
|
|
destination_lang_text = ba.textwidget(parent=self.root_widget,
|
|
position=(40, height - 165),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text="Destination Language : ",
|
|
scale=0.9,
|
|
color=(1, 1, 1))
|
|
|
|
destination_lang_menu = PopupMenu(
|
|
parent=self.root_widget,
|
|
position=(330 if uiscale is ba.UIScale.SMALL else 400, height - 170),
|
|
width=200,
|
|
scale=(2.8 if uiscale is ba.UIScale.SMALL else
|
|
1.8 if uiscale is ba.UIScale.MEDIUM else 1.2),
|
|
current_choice=cfg['Translate Destination Language'],
|
|
choices=list(LANGUAGES.keys())[1:],
|
|
choices_display=list(ba.Lstr(value=i) for i in LANGUAGES.values())[1:],
|
|
button_size=(130, 35),
|
|
on_value_change_call=self._change_destination)
|
|
|
|
try:
|
|
|
|
translation_mode_text = ba.textwidget(parent=self.root_widget,
|
|
position=(40, height - 215),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text="Translate Mode",
|
|
scale=0.9,
|
|
color=(1, 1, 1))
|
|
decoration = ba.textwidget(parent=self.root_widget,
|
|
position=(40, height - 225),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text="________________",
|
|
scale=0.9,
|
|
color=(1, 1, 1))
|
|
|
|
language_char_text = ba.textwidget(parent=self.root_widget,
|
|
position=(85, height - 273),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text='Normal Translation',
|
|
scale=0.6,
|
|
color=(1, 1, 1))
|
|
|
|
pronunciation_text = ba.textwidget(parent=self.root_widget,
|
|
position=(295, height - 273),
|
|
size=(20, 20),
|
|
h_align='left',
|
|
v_align='center',
|
|
text="Show Prononciation",
|
|
scale=0.6,
|
|
color=(1, 1, 1))
|
|
|
|
from bastd.ui.radiogroup import make_radio_group
|
|
cur_val = ba.app.config.get('Pronunciation', True)
|
|
cb1 = ba.checkboxwidget(
|
|
parent=self.root_widget,
|
|
position=(250, height - 275),
|
|
size=(20, 20),
|
|
maxwidth=300,
|
|
scale=1,
|
|
autoselect=True,
|
|
text="")
|
|
cb2 = ba.checkboxwidget(
|
|
parent=self.root_widget,
|
|
position=(40, height - 275),
|
|
size=(20, 20),
|
|
maxwidth=300,
|
|
scale=1,
|
|
autoselect=True,
|
|
text="")
|
|
make_radio_group((cb1, cb2), (True, False), cur_val,
|
|
self._actions_changed)
|
|
except Exception as e:
|
|
print(e)
|
|
pass
|
|
|
|
ba.containerwidget(edit=self.root_widget, cancel_button=btn)
|
|
|
|
def _change_source(self, choice):
|
|
cfg = ba.app.config
|
|
cfg['Translate Source Language'] = choice
|
|
cfg.apply_and_commit()
|
|
|
|
def _change_destination(self, choice):
|
|
cfg = ba.app.config
|
|
cfg['Translate Destination Language'] = choice
|
|
cfg.apply_and_commit()
|
|
|
|
def _actions_changed(self, v: str) -> None:
|
|
cfg = ba.app.config
|
|
cfg['Pronunciation'] = v
|
|
cfg.apply_and_commit()
|
|
|
|
def _cancel(self) -> None:
|
|
ba.containerwidget(edit=self.root_widget, transition='out_scale')
|
|
SettingsWindow()
|
|
|
|
|
|
class SettingsWindow:
|
|
|
|
def __init__(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
height = (300 if uiscale is ba.UIScale.SMALL else
|
|
350 if uiscale is ba.UIScale.MEDIUM else 400)
|
|
width = (500 if uiscale is ba.UIScale.SMALL else
|
|
600 if uiscale is ba.UIScale.MEDIUM else 650)
|
|
scroll_h = (200 if uiscale is ba.UIScale.SMALL else
|
|
250 if uiscale is ba.UIScale.MEDIUM else 270)
|
|
scroll_w = (450 if uiscale is ba.UIScale.SMALL else
|
|
550 if uiscale is ba.UIScale.MEDIUM else 600)
|
|
self._transition_out: Optional[str]
|
|
scale_origin: Optional[Tuple[float, float]]
|
|
self._transition_out = 'out_scale'
|
|
scale_origin = 10
|
|
transition = 'in_scale'
|
|
scale_origin = None
|
|
cancel_is_selected = False
|
|
cfg = ba.app.config
|
|
bg_color = cfg.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
|
|
self.root_widget = ba.containerwidget(
|
|
size=(width, height),
|
|
color=bg_color,
|
|
transition=transition,
|
|
toolbar_visibility='menu_minimal_no_back',
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
on_outside_click_call=self._cancel,
|
|
scale=(2.1 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0),
|
|
scale_origin_stack_offset=scale_origin)
|
|
ba.textwidget(parent=self.root_widget,
|
|
position=(width * 0.5, height - 45),
|
|
size=(20, 20),
|
|
h_align='center',
|
|
v_align='center',
|
|
text="Custom Settings",
|
|
scale=0.9,
|
|
color=(5, 5, 5))
|
|
cbtn = btn = ba.buttonwidget(parent=self.root_widget,
|
|
autoselect=True,
|
|
position=(30, height - 60),
|
|
size=(30, 30),
|
|
label=ba.charstr(ba.SpecialChar.BACK),
|
|
button_type='backSmall',
|
|
on_activate_call=self._cancel)
|
|
scroll_position = (30 if uiscale is ba.UIScale.SMALL else
|
|
40 if uiscale is ba.UIScale.MEDIUM else 50)
|
|
self._scrollwidget = ba.scrollwidget(parent=self.root_widget,
|
|
position=(30, scroll_position),
|
|
simple_culling_v=20.0,
|
|
highlight=False,
|
|
size=(scroll_w, scroll_h),
|
|
selection_loops_to_parent=True)
|
|
ba.widget(edit=self._scrollwidget, right_widget=self._scrollwidget)
|
|
self._subcontainer = ba.columnwidget(parent=self._scrollwidget,
|
|
selection_loops_to_parent=True)
|
|
ip_button = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
size=(300, 30),
|
|
maxwidth=300,
|
|
textcolor=((0, 1, 0) if cfg['IP button'] else (0.95, 0.65, 0)),
|
|
scale=1,
|
|
value=cfg['IP button'],
|
|
autoselect=True,
|
|
text="IP Button",
|
|
on_value_change_call=self.ip_button)
|
|
ping_button = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
size=(300, 30),
|
|
maxwidth=300,
|
|
textcolor=((0, 1, 0) if cfg['ping button'] else (0.95, 0.65, 0)),
|
|
scale=1,
|
|
value=cfg['ping button'],
|
|
autoselect=True,
|
|
text="Ping Button",
|
|
on_value_change_call=self.ping_button)
|
|
copy_button = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
size=(300, 30),
|
|
maxwidth=300,
|
|
textcolor=((0, 1, 0) if cfg['copy button'] else (0.95, 0.65, 0)),
|
|
scale=1,
|
|
value=cfg['copy button'],
|
|
autoselect=True,
|
|
text="Copy Text Button",
|
|
on_value_change_call=self.copy_button)
|
|
direct_send = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
size=(300, 30),
|
|
maxwidth=300,
|
|
textcolor=((0, 1, 0) if cfg['Direct Send'] else (0.95, 0.65, 0)),
|
|
scale=1,
|
|
value=cfg['Direct Send'],
|
|
autoselect=True,
|
|
text="Directly Send Custom Commands",
|
|
on_value_change_call=self.direct_send)
|
|
colorfulchat = ba.checkboxwidget(
|
|
parent=self._subcontainer,
|
|
size=(300, 30),
|
|
maxwidth=300,
|
|
textcolor=((0, 1, 0) if cfg['Colorful Chat'] else (0.95, 0.65, 0)),
|
|
scale=1,
|
|
value=cfg['Colorful Chat'],
|
|
autoselect=True,
|
|
text="Colorful Chat",
|
|
on_value_change_call=self.colorful_chat)
|
|
msg_notification_text = ba.textwidget(parent=self._subcontainer,
|
|
scale=0.8,
|
|
color=(1, 1, 1),
|
|
text='Message Notifcation:',
|
|
size=(100, 30),
|
|
h_align='left',
|
|
v_align='center')
|
|
msg_notification_widget = PopupMenu(
|
|
parent=self._subcontainer,
|
|
position=(100, height - 1200),
|
|
width=200,
|
|
scale=(2.8 if uiscale is ba.UIScale.SMALL else
|
|
1.8 if uiscale is ba.UIScale.MEDIUM else 1.2),
|
|
choices=['top', 'bottom'],
|
|
current_choice=ba.app.config['Message Notification'],
|
|
button_size=(80, 25),
|
|
on_value_change_call=self._change_notification)
|
|
self_status_text = ba.textwidget(parent=self._subcontainer,
|
|
scale=0.8,
|
|
color=(1, 1, 1),
|
|
text='Self Status:',
|
|
size=(100, 30),
|
|
h_align='left',
|
|
v_align='center')
|
|
self_status_widget = PopupMenu(
|
|
parent=self._subcontainer,
|
|
position=(50, height - 1000),
|
|
width=200,
|
|
scale=(2.8 if uiscale is ba.UIScale.SMALL else
|
|
1.8 if uiscale is ba.UIScale.MEDIUM else 1.2),
|
|
choices=['online', 'offline'],
|
|
current_choice=ba.app.config['Self Status'],
|
|
button_size=(80, 25),
|
|
on_value_change_call=self._change_status)
|
|
ba.containerwidget(edit=self.root_widget, cancel_button=btn)
|
|
ba.containerwidget(edit=self.root_widget,
|
|
selected_child=(cbtn if cbtn is not None
|
|
and cancel_is_selected else None),
|
|
start_button=None)
|
|
|
|
self._translation_btn = ba.buttonwidget(parent=self._subcontainer,
|
|
scale=1.2,
|
|
position=(100, 1200),
|
|
size=(150, 50),
|
|
label='Translate Settings',
|
|
on_activate_call=self._translaton_btn,
|
|
autoselect=True)
|
|
|
|
def ip_button(self, value: bool):
|
|
cfg = ba.app.config
|
|
cfg['IP button'] = value
|
|
cfg.apply_and_commit()
|
|
if cfg['IP button']:
|
|
ba.screenmessage("IP Button is now enabled", color=(0, 1, 0))
|
|
else:
|
|
ba.screenmessage("IP Button is now disabled", color=(1, 0.7, 0))
|
|
|
|
def ping_button(self, value: bool):
|
|
cfg = ba.app.config
|
|
cfg['ping button'] = value
|
|
cfg.apply_and_commit()
|
|
if cfg['ping button']:
|
|
ba.screenmessage("Ping Button is now enabled", color=(0, 1, 0))
|
|
else:
|
|
ba.screenmessage("Ping Button is now disabled", color=(1, 0.7, 0))
|
|
|
|
def copy_button(self, value: bool):
|
|
cfg = ba.app.config
|
|
cfg['copy button'] = value
|
|
cfg.apply_and_commit()
|
|
if cfg['copy button']:
|
|
ba.screenmessage("Copy Text Button is now enabled", color=(0, 1, 0))
|
|
else:
|
|
ba.screenmessage("Copy Text Button is now disabled", color=(1, 0.7, 0))
|
|
|
|
def direct_send(self, value: bool):
|
|
cfg = ba.app.config
|
|
cfg['Direct Send'] = value
|
|
cfg.apply_and_commit()
|
|
|
|
def colorful_chat(self, value: bool):
|
|
cfg = ba.app.config
|
|
cfg['Colorful Chat'] = value
|
|
cfg.apply_and_commit()
|
|
|
|
def _change_notification(self, choice):
|
|
cfg = ba.app.config
|
|
cfg['Message Notification'] = choice
|
|
cfg.apply_and_commit()
|
|
|
|
def _change_status(self, choice):
|
|
cfg = ba.app.config
|
|
cfg['Self Status'] = choice
|
|
cfg.apply_and_commit()
|
|
|
|
def _translaton_btn(self):
|
|
try:
|
|
ba.containerwidget(edit=self.root_widget, transition='out_scale')
|
|
TranslationSettings()
|
|
except Exception as e:
|
|
print(e)
|
|
pass
|
|
|
|
def _cancel(self) -> None:
|
|
ba.containerwidget(edit=self.root_widget, transition='out_scale')
|
|
|
|
|
|
class PartyWindow(ba.Window):
|
|
"""Party list/chat window."""
|
|
|
|
def __del__(self) -> None:
|
|
_ba.set_party_window_open(False)
|
|
|
|
def __init__(self, origin: Sequence[float] = (0, 0)):
|
|
self._private_chat = False
|
|
self._firstcall = True
|
|
self.ping_server()
|
|
_ba.set_party_window_open(True)
|
|
self._r = 'partyWindow'
|
|
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)
|
|
self.bg_color = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
self.ping_timer = ba.Timer(5, ba.WeakCall(self.ping_server), repeat=True)
|
|
|
|
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=self.bg_color,
|
|
icon=ba.gettexture('crossOut'),
|
|
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 - 80, self._height - 47),
|
|
size=(50, 50),
|
|
label='...',
|
|
autoselect=True,
|
|
button_type='square',
|
|
on_activate_call=ba.WeakCall(self._on_menu_button_press),
|
|
color=self.bg_color,
|
|
iconscale=1.2)
|
|
|
|
info = _ba.get_connection_to_host_info()
|
|
if info.get('name', '') != '':
|
|
self.title = ba.Lstr(value=info['name'])
|
|
else:
|
|
self.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=self.title,
|
|
size=(0, 0),
|
|
position=(self._width * 0.47,
|
|
self._height - 29),
|
|
maxwidth=self._width * 0.6,
|
|
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,
|
|
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=self.bg_color)
|
|
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=ba.Lstr(resource='chatMutedText'))
|
|
|
|
self._text_field = txt = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(500, 40),
|
|
position=(54, 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)
|
|
|
|
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)
|
|
self._send_button = btn = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 35),
|
|
label=ba.Lstr(resource=self._r + '.sendText'),
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
position=(self._width - 90, 35),
|
|
on_activate_call=self._send_chat_message)
|
|
ba.textwidget(edit=txt, on_return_press_call=btn.activate)
|
|
self._previous_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(30, 30),
|
|
label=ba.charstr(ba.SpecialChar.UP_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(15, 57),
|
|
color=self.bg_color,
|
|
scale=0.75,
|
|
on_activate_call=self._previous_message)
|
|
self._next_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(30, 30),
|
|
label=ba.charstr(ba.SpecialChar.DOWN_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
scale=0.75,
|
|
position=(15, 28),
|
|
on_activate_call=self._next_message)
|
|
self._translate_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(55, 47),
|
|
label="Trans",
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
scale=0.75,
|
|
position=(self._width - 28, 35),
|
|
on_activate_call=self._translate)
|
|
if ba.app.config['copy button']:
|
|
self._copy_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(15, 15),
|
|
label='©',
|
|
button_type='backSmall',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
position=(self._width - 40, 80),
|
|
on_activate_call=self._copy_to_clipboard)
|
|
self._ping_button = None
|
|
if info.get('name', '') != '':
|
|
if ba.app.config['ping button']:
|
|
self._ping_button = ba.buttonwidget(
|
|
parent=self._root_widget,
|
|
scale=0.7,
|
|
position=(self._width - 538, self._height - 57),
|
|
size=(75, 75),
|
|
autoselect=True,
|
|
button_type='square',
|
|
label=f'{_ping}',
|
|
on_activate_call=self._send_ping,
|
|
color=self.bg_color,
|
|
text_scale=2.3,
|
|
iconscale=1.2)
|
|
if ba.app.config['IP button']:
|
|
self._ip_port_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(30, 30),
|
|
label='IP',
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
position=(self._width - 530,
|
|
self._height - 100),
|
|
on_activate_call=self._ip_port_msg)
|
|
self._settings_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 50),
|
|
scale=0.5,
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
position=(self._width - 40, self._height - 47),
|
|
on_activate_call=self._on_setting_button_press,
|
|
icon=ba.gettexture('settingsIcon'),
|
|
iconscale=1.2)
|
|
self._privatechat_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 50),
|
|
scale=0.5,
|
|
button_type='square',
|
|
autoselect=True,
|
|
color=self.bg_color,
|
|
position=(self._width - 40, self._height - 80),
|
|
on_activate_call=self._on_privatechat_button_press,
|
|
icon=ba.gettexture('ouyaOButton'),
|
|
iconscale=1.2)
|
|
self._name_widgets: List[ba.Widget] = []
|
|
self._roster: Optional[List[Dict[str, Any]]] = None
|
|
self._update_timer = ba.Timer(1.0,
|
|
ba.WeakCall(self._update),
|
|
repeat=True,
|
|
timetype=ba.TimeType.REAL)
|
|
self._update()
|
|
|
|
def on_chat_message(self, msg: str, sent=None) -> None:
|
|
"""Called when a new chat message comes through."""
|
|
if ba.app.config['Party Chat Muted'] and not _ba.app.ui.party_window()._private_chat:
|
|
return
|
|
if sent:
|
|
self._add_msg(msg, sent)
|
|
else:
|
|
self._add_msg(msg)
|
|
|
|
def _add_msg(self, msg: str, sent=None) -> None:
|
|
if ba.app.config['Colorful Chat']:
|
|
sender = msg.split(': ')[0]
|
|
color = color_tracker._get_sender_color(sender) if sender else (1, 1, 1)
|
|
else:
|
|
color = (1, 1, 1)
|
|
maxwidth = self._scroll_width * 0.94
|
|
txt = ba.textwidget(parent=self._columnwidget,
|
|
text=msg,
|
|
h_align='left',
|
|
v_align='center',
|
|
size=(0, 13),
|
|
scale=0.55,
|
|
color=color,
|
|
maxwidth=maxwidth,
|
|
shadow=0.3,
|
|
flatness=1.0)
|
|
if sent:
|
|
ba.textwidget(edit=txt, size=(100, 15),
|
|
selectable=True,
|
|
click_activate=True,
|
|
on_activate_call=ba.Call(ba.screenmessage, f'Message sent: {_get_local_time(sent)}'))
|
|
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 _on_menu_button_press(self) -> None:
|
|
is_muted = ba.app.config['Party Chat Muted']
|
|
uiscale = ba.app.ui.uiscale
|
|
|
|
choices = ['muteOption', 'modifyColor', 'addQuickReply', 'removeQuickReply', 'credits']
|
|
choices_display = ['Mute Option', 'Modify Main Color',
|
|
'Add as Quick Reply', 'Remove a Quick Reply', 'Credits']
|
|
|
|
if hasattr(_ba.get_foreground_host_activity(), '_map'):
|
|
choices.append('manualCamera')
|
|
choices_display.append('Manual Camera')
|
|
|
|
PopupMenuWindow(
|
|
position=self._menu_button.get_screen_space_center(),
|
|
color=self.bg_color,
|
|
scale=(2.3 if uiscale is ba.UIScale.SMALL else
|
|
1.65 if uiscale is ba.UIScale.MEDIUM else 1.23),
|
|
choices=choices,
|
|
choices_display=self._create_baLstr_list(choices_display),
|
|
current_choice='muteOption',
|
|
delegate=self)
|
|
self._popup_type = 'menu'
|
|
|
|
def _update(self) -> None:
|
|
if not self._private_chat:
|
|
_ba.set_party_window_open(True)
|
|
ba.textwidget(edit=self._title_text, text=self.title)
|
|
if self._firstcall:
|
|
if hasattr(self, '_status_text'):
|
|
self._status_text.delete()
|
|
self._roster = []
|
|
self._firstcall = False
|
|
self._chat_texts: List[ba.Widget] = []
|
|
if not ba.app.config['Party Chat Muted']:
|
|
msgs = _ba.get_chat_messages()
|
|
for msg in msgs:
|
|
self._add_msg(msg)
|
|
# update muted state
|
|
if ba.app.config['Party 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))
|
|
if self._ping_button:
|
|
ba.buttonwidget(edit=self._ping_button,
|
|
label=f'{_ping}',
|
|
textcolor=self._get_ping_color())
|
|
|
|
# update roster section
|
|
roster = _ba.get_game_roster()
|
|
if roster != self._roster or self._firstcall:
|
|
|
|
self._roster = roster
|
|
|
|
# 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))
|
|
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._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] + '...'
|
|
else:
|
|
p_str = self._roster[index][
|
|
'display_string']
|
|
except Exception:
|
|
ba.print_exception(
|
|
'Error calcing client name str.')
|
|
p_str = '???'
|
|
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)
|
|
|
|
# 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).
|
|
ba.textwidget(edit=widget,
|
|
on_activate_call=ba.Call(
|
|
self._on_party_member_press,
|
|
self._roster[index]['client_id'],
|
|
is_host, widget))
|
|
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)
|
|
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))
|
|
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))
|
|
else:
|
|
_ba.set_party_window_open(False)
|
|
for widget in self._name_widgets:
|
|
widget.delete()
|
|
self._name_widgets = []
|
|
ba.textwidget(edit=self._title_text, text='Private Chat')
|
|
ba.textwidget(edit=self._empty_str, text='')
|
|
if self._firstcall:
|
|
self._firstcall = False
|
|
if hasattr(self, '_status_text'):
|
|
self._status_text.delete()
|
|
try:
|
|
msgs = messenger.pvt_msgs[messenger.filter]
|
|
except:
|
|
msgs = []
|
|
if self._chat_texts:
|
|
while self._chat_texts:
|
|
first = self._chat_texts.pop()
|
|
first.delete()
|
|
uiscale = ba.app.ui.uiscale
|
|
scroll_height = (165 if uiscale is ba.UIScale.SMALL else
|
|
280 if uiscale is ba.UIScale.MEDIUM else 400)
|
|
ba.scrollwidget(edit=self._scrollwidget,
|
|
size=(self._width - 50, scroll_height))
|
|
for msg in msgs:
|
|
message = messenger._format_message(msg)
|
|
self._add_msg(message, msg['sent'])
|
|
self._filter_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.6,
|
|
color=(0.9, 1.0, 0.9),
|
|
text='Filter: ',
|
|
size=(0, 0),
|
|
position=(self._width * 0.3,
|
|
self._height - 70),
|
|
h_align='center',
|
|
v_align='center')
|
|
choices = [i for i in messenger.saved_ids]
|
|
choices_display = [ba.Lstr(value=messenger.saved_ids[i])
|
|
for i in messenger.saved_ids]
|
|
choices.append('add')
|
|
choices_display.append(ba.Lstr(value='***Add New***'))
|
|
filter_widget = PopupMenu(
|
|
parent=self._root_widget,
|
|
position=(self._width * 0.4,
|
|
self._height - 80),
|
|
width=200,
|
|
scale=(2.8 if uiscale is ba.UIScale.SMALL else
|
|
1.8 if uiscale is ba.UIScale.MEDIUM else 1.2),
|
|
choices=choices,
|
|
choices_display=choices_display,
|
|
current_choice=messenger.filter,
|
|
button_size=(120, 30),
|
|
on_value_change_call=self._change_filter)
|
|
self._popup_button = filter_widget.get_button()
|
|
if messenger.filter != 'all':
|
|
user_status = messenger._get_status(messenger.filter)
|
|
if user_status == 'Offline':
|
|
color = (1, 0, 0)
|
|
elif user_status.startswith(('Playing in', 'in Lobby')):
|
|
color = (0, 1, 0)
|
|
else:
|
|
color = (0.9, 1.0, 0.9)
|
|
self._status_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.5,
|
|
color=color,
|
|
text=f'Status: \t{user_status}',
|
|
size=(200, 30),
|
|
position=(self._width * 0.3,
|
|
self._height - 110),
|
|
h_align='center',
|
|
v_align='center',
|
|
autoselect=True,
|
|
selectable=True,
|
|
click_activate=True)
|
|
ba.textwidget(edit=self._status_text,
|
|
on_activate_call=ba.Call(messenger._get_status, messenger.filter, 'last_seen'))
|
|
|
|
def _change_filter(self, choice):
|
|
if choice == 'add':
|
|
self.close()
|
|
AddNewIdWindow()
|
|
else:
|
|
messenger.filter = choice
|
|
self._firstcall = True
|
|
self._filter_text.delete()
|
|
self._popup_button.delete()
|
|
if self._chat_texts:
|
|
while self._chat_texts:
|
|
first = self._chat_texts.pop()
|
|
first.delete()
|
|
self._update()
|
|
|
|
def popup_menu_selected_choice(self, popup_window: PopupMenuWindow,
|
|
choice: str) -> None:
|
|
"""Called when a choice is selected in the popup."""
|
|
if self._popup_type == 'partyMemberPress':
|
|
playerinfo = self._get_player_info(self._popup_party_member_client_id)
|
|
if choice == 'kick':
|
|
name = playerinfo['ds']
|
|
ConfirmWindow(text=f'Are you sure to kick {name}?',
|
|
action=self._vote_kick_player,
|
|
cancel_button=True,
|
|
cancel_is_selected=True,
|
|
color=self.bg_color,
|
|
text_scale=1.0,
|
|
origin_widget=self.get_root_widget())
|
|
elif choice == 'mention':
|
|
players = playerinfo['players']
|
|
choices = []
|
|
namelist = [playerinfo['ds']]
|
|
for player in players:
|
|
name = player['name_full']
|
|
if name not in namelist:
|
|
namelist.append(name)
|
|
choices_display = self._create_baLstr_list(namelist)
|
|
for i in namelist:
|
|
i = i.replace('"', '\"')
|
|
i = i.replace("'", "\'")
|
|
choices.append(f'self._edit_text_msg_box("{i}")')
|
|
PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
|
|
color=self.bg_color,
|
|
scale=self._get_popup_window_scale(),
|
|
choices=choices,
|
|
choices_display=choices_display,
|
|
current_choice=choices[0],
|
|
delegate=self)
|
|
self._popup_type = "executeChoice"
|
|
elif choice == 'adminkick':
|
|
name = playerinfo['ds']
|
|
ConfirmWindow(text=f'Are you sure to use admin\ncommand to kick {name}',
|
|
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 == 'customCommands':
|
|
choices = []
|
|
choices_display = []
|
|
playerinfo = self._get_player_info(self._popup_party_member_client_id)
|
|
account = playerinfo['ds']
|
|
try:
|
|
name = playerinfo['players'][0]['name_full']
|
|
except:
|
|
name = account
|
|
for i in ba.app.config.get('Custom Commands'):
|
|
i = i.replace('$c', str(self._popup_party_member_client_id))
|
|
i = i.replace('$a', str(account))
|
|
i = i.replace('$n', str(name))
|
|
if ba.app.config['Direct Send']:
|
|
choices.append(f'_ba.chatmessage("{i}")')
|
|
else:
|
|
choices.append(f'self._edit_text_msg_box("{i}")')
|
|
choices_display.append(ba.Lstr(value=i))
|
|
choices.append('AddNewChoiceWindow()')
|
|
choices_display.append(ba.Lstr(value='***Add New***'))
|
|
PopupMenuWindow(position=popup_window.root_widget.get_screen_space_center(),
|
|
color=self.bg_color,
|
|
scale=self._get_popup_window_scale(),
|
|
choices=choices,
|
|
choices_display=choices_display,
|
|
current_choice=choices[0],
|
|
delegate=self)
|
|
self._popup_type = 'executeChoice'
|
|
|
|
elif choice == 'addNew':
|
|
AddNewChoiceWindow()
|
|
|
|
elif self._popup_type == 'menu':
|
|
if choice == 'muteOption':
|
|
current_choice = self._get_current_mute_type()
|
|
PopupMenuWindow(
|
|
position=(self._width - 60, self._height - 47),
|
|
color=self.bg_color,
|
|
scale=self._get_popup_window_scale(),
|
|
choices=['muteInGameOnly', 'mutePartyWindowOnly', 'muteAll', 'unmuteAll'],
|
|
choices_display=self._create_baLstr_list(
|
|
['Mute In Game Messages Only', 'Mute Party Window Messages Only', 'Mute all', 'Unmute All']),
|
|
current_choice=current_choice,
|
|
delegate=self
|
|
)
|
|
self._popup_type = 'muteType'
|
|
elif choice == 'modifyColor':
|
|
ColorPickerExact(parent=self.get_root_widget(),
|
|
position=self.get_root_widget().get_screen_space_center(),
|
|
initial_color=self.bg_color,
|
|
delegate=self, tag='')
|
|
elif choice == 'addQuickReply':
|
|
try:
|
|
newReply = ba.textwidget(query=self._text_field)
|
|
oldReplies = self._get_quick_responds()
|
|
oldReplies.append(newReply)
|
|
self._write_quick_responds(oldReplies)
|
|
ba.screenmessage(f'"{newReply}" is added.', (0, 1, 0))
|
|
ba.playsound(ba.getsound('dingSmallHigh'))
|
|
except:
|
|
ba.print_exception()
|
|
elif choice == 'removeQuickReply':
|
|
quick_reply = self._get_quick_responds()
|
|
PopupMenuWindow(position=self._send_button.get_screen_space_center(),
|
|
color=self.bg_color,
|
|
scale=self._get_popup_window_scale(),
|
|
choices=quick_reply,
|
|
choices_display=self._create_baLstr_list(quick_reply),
|
|
current_choice=quick_reply[0],
|
|
delegate=self)
|
|
self._popup_type = 'removeQuickReplySelect'
|
|
elif choice == 'credits':
|
|
ConfirmWindow(
|
|
text=u'\ue043Party Window Reloaded V3\ue043\n\nCredits - Droopy#3730\nSpecial Thanks - BoTT-Vishah#4150',
|
|
action=self.join_discord,
|
|
width=420,
|
|
height=230,
|
|
color=self.bg_color,
|
|
text_scale=1.0,
|
|
ok_text="Join Discord",
|
|
origin_widget=self.get_root_widget())
|
|
elif choice == 'manualCamera':
|
|
ba.containerwidget(edit=self._root_widget, transition='out_scale')
|
|
Manual_camera_window()
|
|
|
|
elif self._popup_type == 'muteType':
|
|
self._change_mute_type(choice)
|
|
|
|
elif self._popup_type == 'executeChoice':
|
|
exec(choice)
|
|
|
|
elif self._popup_type == 'quickMessage':
|
|
if choice == '*** EDIT ORDER ***':
|
|
SortQuickMessages()
|
|
else:
|
|
self._edit_text_msg_box(choice)
|
|
|
|
elif self._popup_type == 'removeQuickReplySelect':
|
|
data = self._get_quick_responds()
|
|
data.remove(choice)
|
|
self._write_quick_responds(data)
|
|
ba.screenmessage(f'"{choice}" is removed.', (1, 0, 0))
|
|
ba.playsound(ba.getsound('shieldDown'))
|
|
|
|
else:
|
|
print(f'unhandled popup type: {self._popup_type}')
|
|
del popup_window # unused
|
|
|
|
def _vote_kick_player(self):
|
|
if self._popup_party_member_is_host:
|
|
ba.playsound(ba.getsound('error'))
|
|
ba.screenmessage(
|
|
ba.Lstr(resource='internal.cantKickHostError'),
|
|
color=(1, 0, 0))
|
|
else:
|
|
assert self._popup_party_member_client_id is not None
|
|
|
|
# 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))
|
|
|
|
def _send_admin_kick_command(self):
|
|
_ba.chatmessage('/kick ' + str(self._popup_party_member_client_id))
|
|
|
|
def _translate(self):
|
|
def _apply_translation(translated):
|
|
if self._text_field.exists():
|
|
ba.textwidget(edit=self._text_field, text=translated)
|
|
msg = ba.textwidget(query=self._text_field)
|
|
cfg = ba.app.config
|
|
if msg == '':
|
|
ba.screenmessage('Nothing to translate.', (1, 0, 0))
|
|
ba.playsound(ba.getsound('error'))
|
|
else:
|
|
data = dict(message=msg)
|
|
if cfg['Translate Source Language']:
|
|
data['src'] = cfg['Translate Source Language']
|
|
if cfg['Translate Destination Language']:
|
|
data['dest'] = cfg['Translate Destination Language']
|
|
if cfg['Pronunciation']:
|
|
data['type'] = 'pronunciation'
|
|
Translate(data, _apply_translation).start()
|
|
|
|
def _copy_to_clipboard(self):
|
|
msg = ba.textwidget(query=self._text_field)
|
|
if msg == '':
|
|
ba.screenmessage('Nothing to copy.', (1, 0, 0))
|
|
ba.playsound(ba.getsound('error'))
|
|
else:
|
|
ba.clipboard_set_text(msg)
|
|
ba.screenmessage(f'"{msg}" is copied to clipboard.', (0, 1, 0))
|
|
ba.playsound(ba.getsound('dingSmallHigh'))
|
|
|
|
def _get_current_mute_type(self):
|
|
cfg = ba.app.config
|
|
if cfg['Chat Muted'] == True:
|
|
if cfg['Party Chat Muted'] == True:
|
|
return 'muteAll'
|
|
else:
|
|
return 'muteInGameOnly'
|
|
else:
|
|
if cfg['Party Chat Muted'] == True:
|
|
return 'mutePartyWindowOnly'
|
|
else:
|
|
return 'unmuteAll'
|
|
|
|
def _change_mute_type(self, choice):
|
|
cfg = ba.app.config
|
|
if choice == 'muteInGameOnly':
|
|
cfg['Chat Muted'] = True
|
|
cfg['Party Chat Muted'] = False
|
|
elif choice == 'mutePartyWindowOnly':
|
|
cfg['Chat Muted'] = False
|
|
cfg['Party Chat Muted'] = True
|
|
elif choice == 'muteAll':
|
|
cfg['Chat Muted'] = True
|
|
cfg['Party Chat Muted'] = True
|
|
else:
|
|
cfg['Chat Muted'] = False
|
|
cfg['Party Chat Muted'] = False
|
|
cfg.apply_and_commit()
|
|
self._update()
|
|
|
|
def popup_menu_closing(self, popup_window: PopupWindow) -> None:
|
|
"""Called when the popup is closing."""
|
|
|
|
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-votes appeared in build 14248
|
|
if (_ba.get_connection_to_host_info().get('build_number', 0) <
|
|
14248):
|
|
return
|
|
kick_str = ba.Lstr(resource='kickVoteText')
|
|
uiscale = ba.app.ui.uiscale
|
|
choices = ['kick', 'mention', 'adminkick']
|
|
choices_display = [kick_str] + \
|
|
list(self._create_baLstr_list(['Mention this guy', f'Kick ID: {client_id}']))
|
|
choices.append('customCommands')
|
|
choices_display.append(ba.Lstr(value='Custom Commands'))
|
|
PopupMenuWindow(
|
|
position=widget.get_screen_space_center(),
|
|
color=self.bg_color,
|
|
scale=(2.3 if uiscale is ba.UIScale.SMALL else
|
|
1.65 if uiscale is ba.UIScale.MEDIUM else 1.23),
|
|
choices=choices,
|
|
choices_display=choices_display,
|
|
current_choice='mention',
|
|
delegate=self)
|
|
self._popup_type = 'partyMemberPress'
|
|
self._popup_party_member_client_id = client_id
|
|
self._popup_party_member_is_host = is_host
|
|
|
|
def _send_chat_message(self) -> None:
|
|
msg = ba.textwidget(query=self._text_field)
|
|
ba.textwidget(edit=self._text_field, text='')
|
|
if '\\' in msg:
|
|
msg = msg.replace('\\d', ('\ue048'))
|
|
msg = msg.replace('\\c', ('\ue043'))
|
|
msg = msg.replace('\\h', ('\ue049'))
|
|
msg = msg.replace('\\s', ('\ue046'))
|
|
msg = msg.replace('\\n', ('\ue04b'))
|
|
msg = msg.replace('\\f', ('\ue04f'))
|
|
msg = msg.replace('\\g', ('\ue027'))
|
|
msg = msg.replace('\\i', ('\ue03a'))
|
|
msg = msg.replace('\\m', ('\ue04d'))
|
|
msg = msg.replace('\\t', ('\ue01f'))
|
|
msg = msg.replace('\\bs', ('\ue01e'))
|
|
msg = msg.replace('\\j', ('\ue010'))
|
|
msg = msg.replace('\\e', ('\ue045'))
|
|
msg = msg.replace('\\l', ('\ue047'))
|
|
msg = msg.replace('\\a', ('\ue020'))
|
|
msg = msg.replace('\\b', ('\ue00c'))
|
|
if not msg:
|
|
choices = self._get_quick_responds()
|
|
choices.append('*** EDIT ORDER ***')
|
|
PopupMenuWindow(position=self._send_button.get_screen_space_center(),
|
|
scale=self._get_popup_window_scale(),
|
|
color=self.bg_color,
|
|
choices=choices,
|
|
current_choice=choices[0],
|
|
delegate=self)
|
|
self._popup_type = 'quickMessage'
|
|
return
|
|
elif msg.startswith('/info '):
|
|
account = msg.replace('/info ', '')
|
|
if account:
|
|
from bastd.ui.account import viewer
|
|
viewer.AccountViewerWindow(
|
|
account_id=account)
|
|
ba.textwidget(edit=self._text_field, text='')
|
|
return
|
|
if not self._private_chat:
|
|
if msg == '/id':
|
|
myid = ba.internal.get_v1_account_misc_read_val_2('resolvedAccountID', '')
|
|
_ba.chatmessage(f"My Unique ID: {myid}")
|
|
elif msg == '/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}@{_port}'] = {
|
|
'addr': _ip,
|
|
'port': _port,
|
|
'name': title
|
|
}
|
|
config.commit()
|
|
ba.screenmessage("Server Added To Manual", color=(0, 1, 0), transient=True)
|
|
ba.playsound(ba.getsound('gunCocking'))
|
|
elif msg != '':
|
|
_ba.chatmessage(cast(str, msg))
|
|
else:
|
|
receiver = messenger.filter
|
|
name = ba.internal.get_v1_account_display_string()
|
|
if not receiver:
|
|
display_error('Choose a valid receiver id')
|
|
return
|
|
data = {'receiver': receiver, 'message': f'{name}: {msg}'}
|
|
if msg.startswith('/rename '):
|
|
if messenger.filter != 'all':
|
|
nickname = msg.replace('/rename ', '')
|
|
messenger._save_id(messenger.filter, nickname, verify=False)
|
|
self._change_filter(messenger.filter)
|
|
elif msg == '/remove':
|
|
if messenger.filter != 'all':
|
|
messenger._remove_id(messenger.filter)
|
|
self._change_filter('all')
|
|
else:
|
|
display_error('Cant delete this')
|
|
ba.textwidget(edit=self._text_field, text='')
|
|
return
|
|
ba.Call(messenger._send_request, url, data)
|
|
ba.Call(check_new_message)
|
|
Thread(target=messenger._send_request, args=(url, data)).start()
|
|
Thread(target=check_new_message).start()
|
|
ba.textwidget(edit=self._text_field, text='')
|
|
|
|
def _write_quick_responds(self, data):
|
|
try:
|
|
with open(quick_msg_file, 'w') as f:
|
|
f.write('\n'.join(data))
|
|
except:
|
|
ba.print_exception()
|
|
ba.screenmessage('Error!', (1, 0, 0))
|
|
ba.playsound(ba.getsound('error'))
|
|
|
|
def _get_quick_responds(self):
|
|
if os.path.exists(quick_msg_file):
|
|
with open(quick_msg_file, 'r') as f:
|
|
return f.read().split('\n')
|
|
else:
|
|
default_replies = ['What the hell?', 'Dude that\'s amazing!']
|
|
self._write_quick_responds(default_replies)
|
|
return default_replies
|
|
|
|
def color_picker_selected_color(self, picker, color) -> None:
|
|
ba.containerwidget(edit=self._root_widget, color=color)
|
|
color = tuple(round(i, 2) for i in color)
|
|
self.bg_color = color
|
|
ba.app.config['PartyWindow Main Color'] = color
|
|
|
|
def color_picker_closing(self, picker) -> None:
|
|
ba.app.config.apply_and_commit()
|
|
|
|
def _remove_sender_from_message(self, msg=''):
|
|
msg_start = msg.find(": ") + 2
|
|
return msg[msg_start:]
|
|
|
|
def _previous_message(self):
|
|
msgs = self._chat_texts
|
|
if not hasattr(self, 'msg_index'):
|
|
self.msg_index = len(msgs) - 1
|
|
else:
|
|
if self.msg_index > 0:
|
|
self.msg_index -= 1
|
|
else:
|
|
del self.msg_index
|
|
try:
|
|
msg_widget = msgs[self.msg_index]
|
|
msg = ba.textwidget(query=msg_widget)
|
|
msg = self._remove_sender_from_message(msg)
|
|
if msg in ('', ' '):
|
|
self._previous_message()
|
|
return
|
|
except:
|
|
msg = ''
|
|
self._edit_text_msg_box(msg, 'replace')
|
|
|
|
def _next_message(self):
|
|
msgs = self._chat_texts
|
|
if not hasattr(self, 'msg_index'):
|
|
self.msg_index = 0
|
|
else:
|
|
if self.msg_index < len(msgs) - 1:
|
|
self.msg_index += 1
|
|
else:
|
|
del self.msg_index
|
|
try:
|
|
msg_widget = msgs[self.msg_index]
|
|
msg = ba.textwidget(query=msg_widget)
|
|
msg = self._remove_sender_from_message(msg)
|
|
if msg in ('', ' '):
|
|
self._next_message()
|
|
return
|
|
except:
|
|
msg = ''
|
|
self._edit_text_msg_box(msg, 'replace')
|
|
|
|
def _ip_port_msg(self):
|
|
try:
|
|
msg = f'IP: {_ip} PORT: {_port}'
|
|
except:
|
|
msg = ''
|
|
self._edit_text_msg_box(msg, 'replace')
|
|
|
|
def ping_server(self):
|
|
info = _ba.get_connection_to_host_info()
|
|
if info.get('name', '') != '':
|
|
self.pingThread = PingThread(_ip, _port)
|
|
self.pingThread.start()
|
|
|
|
def _get_ping_color(self):
|
|
try:
|
|
if _ping < 100:
|
|
return (0, 1, 0)
|
|
elif _ping < 500:
|
|
return (1, 1, 0)
|
|
else:
|
|
return (1, 0, 0)
|
|
except:
|
|
return (0.1, 0.1, 0.1)
|
|
|
|
def _send_ping(self):
|
|
if isinstance(_ping, int):
|
|
_ba.chatmessage(f'My ping = {_ping}ms')
|
|
|
|
def close(self) -> None:
|
|
"""Close the window."""
|
|
ba.containerwidget(edit=self._root_widget, transition='out_scale')
|
|
|
|
def close_with_sound(self) -> None:
|
|
"""Close the window and make a lovely sound."""
|
|
ba.playsound(ba.getsound('swish'))
|
|
self.close()
|
|
|
|
def _get_popup_window_scale(self) -> float:
|
|
uiscale = ba.app.ui.uiscale
|
|
return (2.4 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0)
|
|
|
|
def _create_baLstr_list(self, list1):
|
|
return (ba.Lstr(value=i) for i in list1)
|
|
|
|
def _get_player_info(self, clientID):
|
|
info = {}
|
|
for i in _ba.get_game_roster():
|
|
if i['client_id'] == clientID:
|
|
info['ds'] = i['display_string']
|
|
info['players'] = i['players']
|
|
info['aid'] = i['account_id']
|
|
break
|
|
return info
|
|
|
|
def _edit_text_msg_box(self, text, action='add'):
|
|
if isinstance(text, str):
|
|
if action == 'add':
|
|
ba.textwidget(edit=self._text_field, text=ba.textwidget(
|
|
query=self._text_field) + text)
|
|
elif action == 'replace':
|
|
ba.textwidget(edit=self._text_field, text=text)
|
|
|
|
def _on_setting_button_press(self):
|
|
try:
|
|
SettingsWindow()
|
|
except Exception as e:
|
|
ba.print_exception()
|
|
pass
|
|
|
|
def _on_privatechat_button_press(self):
|
|
try:
|
|
if messenger.logged_in:
|
|
self._firstcall = True
|
|
if self._chat_texts:
|
|
while self._chat_texts:
|
|
first = self._chat_texts.pop()
|
|
first.delete()
|
|
if not self._private_chat:
|
|
self._private_chat = True
|
|
else:
|
|
self._filter_text.delete()
|
|
self._popup_button.delete()
|
|
self._private_chat = False
|
|
self._update()
|
|
else:
|
|
if messenger.server_online:
|
|
if not messenger._cookie_login():
|
|
if messenger._query():
|
|
LoginWindow(wtype='login')
|
|
else:
|
|
LoginWindow(wtype='signup')
|
|
else:
|
|
display_error(messenger.error)
|
|
except Exception as e:
|
|
ba.print_exception()
|
|
pass
|
|
|
|
def join_discord(self):
|
|
ba.open_url("https://discord.gg/KvYgpEg2JR")
|
|
|
|
|
|
class LoginWindow:
|
|
def __init__(self, wtype):
|
|
self.wtype = wtype
|
|
if self.wtype == 'signup':
|
|
title = 'Sign Up Window'
|
|
label = 'Sign Up'
|
|
else:
|
|
title = 'Login Window'
|
|
label = 'Log In'
|
|
uiscale = ba.app.ui.uiscale
|
|
bg_color = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
self._root_widget = ba.containerwidget(size=(500, 250),
|
|
transition='in_scale',
|
|
color=bg_color,
|
|
toolbar_visibility='menu_minimal_no_back',
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
on_outside_click_call=self._close,
|
|
scale=(2.1 if uiscale is ba.UIScale.SMALL else
|
|
1.5 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._title_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.8,
|
|
color=(1, 1, 1),
|
|
text=title,
|
|
size=(0, 0),
|
|
position=(250, 200),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._id = ba.textwidget(parent=self._root_widget,
|
|
scale=0.5,
|
|
color=(1, 1, 1),
|
|
text=f'Account: ' +
|
|
ba.internal.get_v1_account_misc_read_val_2(
|
|
'resolvedAccountID', ''),
|
|
size=(0, 0),
|
|
position=(220, 170),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._registrationkey_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.5,
|
|
color=(1, 1, 1),
|
|
text=f'Registration Key: ',
|
|
size=(0, 0),
|
|
position=(100, 140),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._text_field = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(200, 40),
|
|
position=(175, 130),
|
|
text='',
|
|
maxwidth=410,
|
|
flatness=1.0,
|
|
autoselect=True,
|
|
v_align='center',
|
|
corner_scale=0.7)
|
|
self._connect_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(150, 30),
|
|
color=(0, 1, 0),
|
|
label='Get Registration Key',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(150, 80),
|
|
on_activate_call=self._connect)
|
|
self._confirm_button = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label=label,
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(200, 40),
|
|
on_activate_call=self._confirmcall)
|
|
ba.textwidget(edit=self._text_field, on_return_press_call=self._confirm_button.activate)
|
|
|
|
def _close(self):
|
|
ba.containerwidget(edit=self._root_widget,
|
|
transition=('out_scale'))
|
|
|
|
def _connect(self):
|
|
try:
|
|
host = url.split('http://')[1].split(':')[0]
|
|
import socket
|
|
address = socket.gethostbyname(host)
|
|
_ba.disconnect_from_host()
|
|
_ba.connect_to_party(address, port=11111)
|
|
except Exception:
|
|
display_error('Cant get ip from hostname')
|
|
|
|
def _confirmcall(self):
|
|
if self.wtype == 'signup':
|
|
key = ba.textwidget(query=self._text_field)
|
|
answer = messenger._signup(registration_key=key) if key else None
|
|
if answer:
|
|
self._close()
|
|
else:
|
|
if messenger._login(registration_key=ba.textwidget(query=self._text_field)):
|
|
self._close()
|
|
|
|
|
|
class AddNewIdWindow:
|
|
def __init__(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
bg_color = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
self._root_widget = ba.containerwidget(size=(500, 250),
|
|
transition='in_scale',
|
|
color=bg_color,
|
|
toolbar_visibility='menu_minimal_no_back',
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
on_outside_click_call=self._close,
|
|
scale=(2.1 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0))
|
|
self._title_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.8,
|
|
color=(1, 1, 1),
|
|
text='Add New ID',
|
|
size=(0, 0),
|
|
position=(250, 200),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._accountid_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.6,
|
|
color=(1, 1, 1),
|
|
text='pb-id: ',
|
|
size=(0, 0),
|
|
position=(50, 155),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._accountid_field = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(250, 40),
|
|
position=(100, 140),
|
|
text='',
|
|
maxwidth=410,
|
|
flatness=1.0,
|
|
autoselect=True,
|
|
v_align='center',
|
|
corner_scale=0.7)
|
|
self._nickname_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.5,
|
|
color=(1, 1, 1),
|
|
text='Nickname: ',
|
|
size=(0, 0),
|
|
position=(50, 115),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._nickname_field = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(250, 40),
|
|
position=(100, 100),
|
|
text='<default>',
|
|
maxwidth=410,
|
|
flatness=1.0,
|
|
autoselect=True,
|
|
v_align='center',
|
|
corner_scale=0.7)
|
|
self._help_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.4,
|
|
color=(0.1, 0.9, 0.9),
|
|
text='Help:\nEnter pb-id of account you\n want to chat to\nEnter nickname of id to\n recognize id easily\nLeave nickname <default>\n to use their default name',
|
|
size=(0, 0),
|
|
position=(325, 120),
|
|
h_align='left',
|
|
v_align='center')
|
|
self._add = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label='Add',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(100, 50),
|
|
on_activate_call=ba.Call(self._relay_function))
|
|
ba.textwidget(edit=self._accountid_field, on_return_press_call=self._add.activate)
|
|
self._remove = ba.buttonwidget(parent=self._root_widget,
|
|
size=(75, 30),
|
|
label='Remove',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(170, 50),
|
|
on_activate_call=self._remove_id)
|
|
ba.containerwidget(edit=self._root_widget,
|
|
on_cancel_call=self._close)
|
|
|
|
def _relay_function(self):
|
|
account_id = ba.textwidget(query=self._accountid_field)
|
|
nickname = ba.textwidget(query=self._nickname_field)
|
|
try:
|
|
if messenger._save_id(account_id, nickname):
|
|
self._close()
|
|
except:
|
|
display_error('Enter valid pb-id')
|
|
|
|
def _remove_id(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
if len(messenger.saved_ids) > 1:
|
|
choices = [i for i in messenger.saved_ids]
|
|
choices.remove('all')
|
|
choices_display = [ba.Lstr(value=messenger.saved_ids[i]) for i in choices]
|
|
PopupMenuWindow(position=self._remove.get_screen_space_center(),
|
|
color=ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5)),
|
|
scale=(2.4 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0),
|
|
choices=choices,
|
|
choices_display=choices_display,
|
|
current_choice=choices[0],
|
|
delegate=self)
|
|
self._popup_type = 'removeSelectedID'
|
|
|
|
def popup_menu_selected_choice(self, popup_window: PopupMenuWindow,
|
|
choice: str) -> None:
|
|
"""Called when a choice is selected in the popup."""
|
|
if self._popup_type == 'removeSelectedID':
|
|
messenger._remove_id(choice)
|
|
self._close()
|
|
|
|
def popup_menu_closing(self, popup_window: PopupWindow) -> None:
|
|
"""Called when the popup is closing."""
|
|
|
|
def _close(self):
|
|
ba.containerwidget(edit=self._root_widget,
|
|
transition=('out_scale'))
|
|
|
|
|
|
class AddNewChoiceWindow:
|
|
def __init__(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
bg_color = ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5))
|
|
self._root_widget = ba.containerwidget(size=(500, 250),
|
|
transition='in_scale',
|
|
color=bg_color,
|
|
toolbar_visibility='menu_minimal_no_back',
|
|
parent=_ba.get_special_widget('overlay_stack'),
|
|
on_outside_click_call=self._close,
|
|
scale=(2.1 if uiscale is ba.UIScale.SMALL else
|
|
1.5 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._title_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.8,
|
|
color=(1, 1, 1),
|
|
text='Add Custom Command',
|
|
size=(0, 0),
|
|
position=(250, 200),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._text_field = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(500, 40),
|
|
position=(75, 140),
|
|
text='',
|
|
maxwidth=410,
|
|
flatness=1.0,
|
|
autoselect=True,
|
|
v_align='center',
|
|
corner_scale=0.7)
|
|
self._help_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.4,
|
|
color=(0.2, 0.2, 0.2),
|
|
text='Use\n$c = client id\n$a = account id\n$n = name',
|
|
size=(0, 0),
|
|
position=(70, 75),
|
|
h_align='left',
|
|
v_align='center')
|
|
self._add = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label='Add',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(150, 50),
|
|
on_activate_call=self._add_choice)
|
|
ba.textwidget(edit=self._text_field, on_return_press_call=self._add.activate)
|
|
self._remove = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label='Remove',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(350, 50),
|
|
on_activate_call=self._remove_custom_command)
|
|
ba.containerwidget(edit=self._root_widget,
|
|
on_cancel_call=self._close)
|
|
|
|
def _add_choice(self):
|
|
newCommand = ba.textwidget(query=self._text_field)
|
|
cfg = ba.app.config
|
|
if any(i in newCommand for i in ('$c', '$a', '$n')):
|
|
cfg['Custom Commands'].append(newCommand)
|
|
cfg.apply_and_commit()
|
|
ba.screenmessage('Added successfully', (0, 1, 0))
|
|
ba.playsound(ba.getsound('dingSmallHigh'))
|
|
self._close()
|
|
else:
|
|
ba.screenmessage('Use at least of these ($c, $a, $n)', (1, 0, 0))
|
|
ba.playsound(ba.getsound('error'))
|
|
|
|
def _remove_custom_command(self):
|
|
uiscale = ba.app.ui.uiscale
|
|
commands = ba.app.config['Custom Commands']
|
|
PopupMenuWindow(position=self._remove.get_screen_space_center(),
|
|
color=ba.app.config.get('PartyWindow Main Color', (0.5, 0.5, 0.5)),
|
|
scale=(2.4 if uiscale is ba.UIScale.SMALL else
|
|
1.5 if uiscale is ba.UIScale.MEDIUM else 1.0),
|
|
choices=commands,
|
|
current_choice=commands[0],
|
|
delegate=self)
|
|
self._popup_type = 'removeCustomCommandSelect'
|
|
|
|
def popup_menu_selected_choice(self, popup_window: PopupMenuWindow,
|
|
choice: str) -> None:
|
|
"""Called when a choice is selected in the popup."""
|
|
if self._popup_type == 'removeCustomCommandSelect':
|
|
config = ba.app.config
|
|
config['Custom Commands'].remove(choice)
|
|
config.apply_and_commit()
|
|
ba.screenmessage('Removed successfully', (0, 1, 0))
|
|
ba.playsound(ba.getsound('shieldDown'))
|
|
|
|
def popup_menu_closing(self, popup_window: PopupWindow) -> None:
|
|
"""Called when the popup is closing."""
|
|
|
|
def _close(self):
|
|
ba.containerwidget(edit=self._root_widget,
|
|
transition=('out_scale'))
|
|
|
|
|
|
class Manual_camera_window:
|
|
def __init__(self):
|
|
self._root_widget = ba.containerwidget(
|
|
on_outside_click_call=None,
|
|
size=(0, 0))
|
|
button_size = (30, 30)
|
|
self._title_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.9,
|
|
color=(1, 1, 1),
|
|
text='Manual Camera Setup',
|
|
size=(0, 0),
|
|
position=(130, 153),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._xminus = ba.buttonwidget(parent=self._root_widget,
|
|
size=button_size,
|
|
label=ba.charstr(ba.SpecialChar.LEFT_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(1, 60),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'x-'))
|
|
self._xplus = ba.buttonwidget(parent=self._root_widget,
|
|
size=button_size,
|
|
label=ba.charstr(ba.SpecialChar.RIGHT_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(60, 60),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'x'))
|
|
self._yplus = ba.buttonwidget(parent=self._root_widget,
|
|
size=button_size,
|
|
label=ba.charstr(ba.SpecialChar.UP_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(30, 100),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'y'))
|
|
self._yminus = ba.buttonwidget(parent=self._root_widget,
|
|
size=button_size,
|
|
label=ba.charstr(ba.SpecialChar.DOWN_ARROW),
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(30, 20),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'y-'))
|
|
self.inwards = ba.buttonwidget(parent=self._root_widget,
|
|
size=(100, 30),
|
|
label='INWARDS',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(120, 90),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'z-'))
|
|
self._outwards = ba.buttonwidget(parent=self._root_widget,
|
|
size=(100, 30),
|
|
label='OUTWARDS',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(120, 50),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'z'))
|
|
self._step_text = ba.textwidget(parent=self._root_widget,
|
|
scale=0.5,
|
|
color=(1, 1, 1),
|
|
text='Step:',
|
|
size=(0, 0),
|
|
position=(1, -20),
|
|
h_align='center',
|
|
v_align='center')
|
|
self._text_field = ba.textwidget(
|
|
parent=self._root_widget,
|
|
editable=True,
|
|
size=(100, 40),
|
|
position=(26, -35),
|
|
text='',
|
|
maxwidth=120,
|
|
flatness=1.0,
|
|
autoselect=True,
|
|
v_align='center',
|
|
corner_scale=0.7)
|
|
self._reset = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label='Reset',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(120, -35),
|
|
on_activate_call=ba.Call(self._change_camera_position, 'reset'))
|
|
self._done = ba.buttonwidget(parent=self._root_widget,
|
|
size=(50, 30),
|
|
label='Done',
|
|
button_type='square',
|
|
autoselect=True,
|
|
position=(180, -35),
|
|
on_activate_call=self._close)
|
|
ba.containerwidget(edit=self._root_widget,
|
|
cancel_button=self._done)
|
|
|
|
def _close(self):
|
|
ba.containerwidget(edit=self._root_widget,
|
|
transition=('out_scale'))
|
|
|
|
def _change_camera_position(self, direction):
|
|
activity = _ba.get_foreground_host_activity()
|
|
node = activity.globalsnode
|
|
aoi = list(node.area_of_interest_bounds)
|
|
center = [(aoi[0] + aoi[3]) / 2,
|
|
(aoi[1] + aoi[4]) / 2,
|
|
(aoi[2] + aoi[5]) / 2]
|
|
size = (aoi[3] - aoi[0],
|
|
aoi[4] - aoi[1],
|
|
aoi[5] - aoi[2])
|
|
|
|
try:
|
|
increment = float(ba.textwidget(query=self._text_field))
|
|
except:
|
|
# ba.print_exception()
|
|
increment = 1
|
|
|
|
if direction == 'x':
|
|
center[0] += increment
|
|
elif direction == 'x-':
|
|
center[0] -= increment
|
|
elif direction == 'y':
|
|
center[1] += increment
|
|
elif direction == 'y-':
|
|
center[1] -= increment
|
|
elif direction == 'z':
|
|
center[2] += increment
|
|
elif direction == 'z-':
|
|
center[2] -= increment
|
|
elif direction == 'reset':
|
|
node.area_of_interest_bounds = activity._map.get_def_bound_box(
|
|
'area_of_interest_bounds')
|
|
return
|
|
|
|
aoi = (center[0] - size[0] / 2,
|
|
center[1] - size[1] / 2,
|
|
center[2] - size[2] / 2,
|
|
center[0] + size[0] / 2,
|
|
center[1] + size[1] / 2,
|
|
center[2] + size[2] / 2)
|
|
node.area_of_interest_bounds = tuple(aoi)
|
|
|
|
|
|
def __popup_menu_window_init__(self,
|
|
position: Tuple[float, float],
|
|
choices: Sequence[str],
|
|
current_choice: str,
|
|
delegate: Any = None,
|
|
width: float = 230.0,
|
|
maxwidth: float = None,
|
|
scale: float = 1.0,
|
|
color: Tuple[float, float, float] = (0.35, 0.55, 0.15),
|
|
choices_disabled: Sequence[str] = None,
|
|
choices_display: Sequence[ba.Lstr] = None):
|
|
# FIXME: Clean up a bit.
|
|
# pylint: disable=too-many-branches
|
|
# pylint: disable=too-many-locals
|
|
# pylint: disable=too-many-statements
|
|
if choices_disabled is None:
|
|
choices_disabled = []
|
|
if choices_display is None:
|
|
choices_display = []
|
|
|
|
# FIXME: For the moment we base our width on these strings so
|
|
# we need to flatten them.
|
|
choices_display_fin: List[str] = []
|
|
for choice_display in choices_display:
|
|
choices_display_fin.append(choice_display.evaluate())
|
|
|
|
if maxwidth is None:
|
|
maxwidth = width * 1.5
|
|
|
|
self._transitioning_out = False
|
|
self._choices = list(choices)
|
|
self._choices_display = list(choices_display_fin)
|
|
self._current_choice = current_choice
|
|
self._color = color
|
|
self._choices_disabled = list(choices_disabled)
|
|
self._done_building = False
|
|
if not choices:
|
|
raise TypeError('Must pass at least one choice')
|
|
self._width = width
|
|
self._scale = scale
|
|
if len(choices) > 8:
|
|
self._height = 280
|
|
self._use_scroll = True
|
|
else:
|
|
self._height = 20 + len(choices) * 33
|
|
self._use_scroll = False
|
|
self._delegate = None # don't want this stuff called just yet..
|
|
|
|
# extend width to fit our longest string (or our max-width)
|
|
for index, choice in enumerate(choices):
|
|
if len(choices_display_fin) == len(choices):
|
|
choice_display_name = choices_display_fin[index]
|
|
else:
|
|
choice_display_name = choice
|
|
if self._use_scroll:
|
|
self._width = max(
|
|
self._width,
|
|
min(
|
|
maxwidth,
|
|
_ba.get_string_width(choice_display_name,
|
|
suppress_warning=True)) + 75)
|
|
else:
|
|
self._width = max(
|
|
self._width,
|
|
min(
|
|
maxwidth,
|
|
_ba.get_string_width(choice_display_name,
|
|
suppress_warning=True)) + 60)
|
|
|
|
# init parent class - this will rescale and reposition things as
|
|
# needed and create our root widget
|
|
PopupWindow.__init__(self,
|
|
position,
|
|
size=(self._width, self._height),
|
|
bg_color=self._color,
|
|
scale=self._scale)
|
|
|
|
if self._use_scroll:
|
|
self._scrollwidget = ba.scrollwidget(parent=self.root_widget,
|
|
position=(20, 20),
|
|
highlight=False,
|
|
color=(0.35, 0.55, 0.15),
|
|
size=(self._width - 40,
|
|
self._height - 40))
|
|
self._columnwidget = ba.columnwidget(parent=self._scrollwidget,
|
|
border=2,
|
|
margin=0)
|
|
else:
|
|
self._offset_widget = ba.containerwidget(parent=self.root_widget,
|
|
position=(30, 15),
|
|
size=(self._width - 40,
|
|
self._height),
|
|
background=False)
|
|
self._columnwidget = ba.columnwidget(parent=self._offset_widget,
|
|
border=2,
|
|
margin=0)
|
|
for index, choice in enumerate(choices):
|
|
if len(choices_display_fin) == len(choices):
|
|
choice_display_name = choices_display_fin[index]
|
|
else:
|
|
choice_display_name = choice
|
|
inactive = (choice in self._choices_disabled)
|
|
wdg = ba.textwidget(parent=self._columnwidget,
|
|
size=(self._width - 40, 28),
|
|
on_select_call=ba.Call(self._select, index),
|
|
click_activate=True,
|
|
color=(0.5, 0.5, 0.5, 0.5) if inactive else
|
|
((0.5, 1, 0.5,
|
|
1) if choice == self._current_choice else
|
|
(0.8, 0.8, 0.8, 1.0)),
|
|
padding=0,
|
|
maxwidth=maxwidth,
|
|
text=choice_display_name,
|
|
on_activate_call=self._activate,
|
|
v_align='center',
|
|
selectable=(not inactive))
|
|
if choice == self._current_choice:
|
|
ba.containerwidget(edit=self._columnwidget,
|
|
selected_child=wdg,
|
|
visible_child=wdg)
|
|
|
|
# ok from now on our delegate can be called
|
|
self._delegate = weakref.ref(delegate)
|
|
self._done_building = True
|
|
|
|
|
|
original_connect_to_party = _ba.connect_to_party
|
|
original_sign_in = ba.internal.sign_in_v1
|
|
|
|
|
|
def modify_connect_to_party(address: str, port: int = 43210, print_progress: bool = True) -> None:
|
|
global _ip, _port
|
|
_ip = address
|
|
_port = port
|
|
original_connect_to_party(_ip, _port, print_progress)
|
|
|
|
|
|
temptimer = None
|
|
|
|
|
|
def modify_sign_in(account_type: str) -> None:
|
|
original_sign_in(account_type)
|
|
if messenger.server_online:
|
|
messenger.logged_in = False
|
|
global temptimer
|
|
temptimer = ba.Timer(2, messenger._cookie_login)
|
|
|
|
|
|
class PingThread(Thread):
|
|
"""Thread for sending out game pings."""
|
|
|
|
def __init__(self, address: str, port: int):
|
|
super().__init__()
|
|
self._address = address
|
|
self._port = port
|
|
|
|
def run(self) -> None:
|
|
sock: Optional[socket.socket] = None
|
|
try:
|
|
import socket
|
|
from ba.internal import get_ip_address_type
|
|
socket_type = get_ip_address_type(self._address)
|
|
sock = socket.socket(socket_type, socket.SOCK_DGRAM)
|
|
sock.connect((self._address, self._port))
|
|
|
|
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)
|
|
global _ping
|
|
_ping = int((time.time() - starttime) * 1000.0)
|
|
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)
|
|
|
|
|
|
def _get_store_char_tex(self) -> str:
|
|
_ba.set_party_icon_always_visible(True)
|
|
return ('storeCharacterXmas' if ba.internal.get_v1_account_misc_read_val(
|
|
'xmas', False) else
|
|
'storeCharacterEaster' if ba.internal.get_v1_account_misc_read_val(
|
|
'easter', False) else 'storeCharacter')
|
|
|
|
|
|
# ba_meta export plugin
|
|
class InitalRun(ba.Plugin):
|
|
def __init__(self):
|
|
if _ba.env().get("build_number", 0) >= 20124:
|
|
global messenger, listener, displayer, color_tracker
|
|
initialize()
|
|
messenger = PrivateChatHandler()
|
|
listener = Thread(target=messenger_thread)
|
|
listener.start()
|
|
displayer = ba.Timer(0.4, msg_displayer, True)
|
|
color_tracker = ColorTracker()
|
|
bastd.ui.party.PartyWindow = PartyWindow
|
|
PopupMenuWindow.__init__ = __popup_menu_window_init__
|
|
_ba.connect_to_party = modify_connect_to_party
|
|
ba.internal.sign_in_v1 = modify_sign_in
|
|
MainMenuWindow._get_store_char_tex = _get_store_char_tex
|
|
else:
|
|
display_error("This Party Window only runs with BombSquad version higer than 1.6.0.")
|