|
|
|
@ -28,19 +28,19 @@ class Bind(object):
|
|
|
|
|
bindTypes = []
|
|
|
|
|
instances = {}
|
|
|
|
|
|
|
|
|
|
def __init__(self, key='', fields={}, *, parent=None):
|
|
|
|
|
def __init__(self, key='', fields={}, /,*, parent=None):
|
|
|
|
|
if parent is None:
|
|
|
|
|
self.alias = False
|
|
|
|
|
self.key = str(key)
|
|
|
|
|
self.fields = fields
|
|
|
|
|
self.errors = []
|
|
|
|
|
self.warnings = []
|
|
|
|
|
self.alias = False
|
|
|
|
|
self.key = str(key)
|
|
|
|
|
self.fields = fields
|
|
|
|
|
self.errors = []
|
|
|
|
|
self.warnings = []
|
|
|
|
|
self.TargetType = None
|
|
|
|
|
else:
|
|
|
|
|
self.alias = parent.alias
|
|
|
|
|
self.key = parent.key
|
|
|
|
|
self.fields = parent.fields
|
|
|
|
|
self.errors = parent.errors
|
|
|
|
|
self.alias = parent.alias
|
|
|
|
|
self.key = parent.key
|
|
|
|
|
self.fields = parent.fields
|
|
|
|
|
self.errors = parent.errors
|
|
|
|
|
self.warnings = parent.warnings
|
|
|
|
|
|
|
|
|
|
# redefined for each unique type, default just verifies key
|
|
|
|
@ -72,15 +72,11 @@ class Bind(object):
|
|
|
|
|
self.instances[type(self)] = [self]
|
|
|
|
|
|
|
|
|
|
def verify(self):
|
|
|
|
|
try:
|
|
|
|
|
self.alias = self.fields.pop('alias')
|
|
|
|
|
if not isinstance(self.alias, bool):
|
|
|
|
|
self.errors.append(
|
|
|
|
|
f'alias should be "yes" or "no", not "{self.alias}"'
|
|
|
|
|
)
|
|
|
|
|
self.alias = False
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.alias = False
|
|
|
|
|
self.alias = self.optional('alias', default=False)
|
|
|
|
|
if not isinstance(self.alias, bool):
|
|
|
|
|
self.err(
|
|
|
|
|
f'`alias` should be "yes" or "no", not "{self.alias}"'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
typeName, self.key = self.key.split(' ', 1)
|
|
|
|
@ -107,6 +103,21 @@ class Bind(object):
|
|
|
|
|
if (not self.alias) and (self.key not in validKeyList):
|
|
|
|
|
self.errors.append(f'invalid key name: "{self.key}"')
|
|
|
|
|
|
|
|
|
|
def optional(self, name, /,*, default=None):
|
|
|
|
|
try:
|
|
|
|
|
return self.fields.pop(name)
|
|
|
|
|
except popErrors:
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
def cmdListFrom(self, name, /,*, default=None):
|
|
|
|
|
result = self.fields.pop(name)
|
|
|
|
|
if isinstance(result, str):
|
|
|
|
|
return result.split(';')
|
|
|
|
|
elif isinstance(result, list):
|
|
|
|
|
return result
|
|
|
|
|
else:
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
def toTargetType(self):
|
|
|
|
|
if self.TargetType is None:
|
|
|
|
|
# do nothing
|
|
|
|
@ -128,21 +139,20 @@ class Bind(object):
|
|
|
|
|
|
|
|
|
|
class Impulse(Bind):
|
|
|
|
|
def verify(self):
|
|
|
|
|
self.command = None
|
|
|
|
|
self.command: list = None
|
|
|
|
|
if not isinstance(self.fields, dict):
|
|
|
|
|
self.fields = {'command': self.fields}
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.command = self.fields.pop('command')
|
|
|
|
|
if isinstance(self.command, str):
|
|
|
|
|
self.command = self.command.split(';')
|
|
|
|
|
elif not isinstance(self.command, list):
|
|
|
|
|
self.err('`command` field must be argument of string or list')
|
|
|
|
|
self.command = None
|
|
|
|
|
except KeyError:
|
|
|
|
|
self.command = self.cmdListFrom(
|
|
|
|
|
'command',
|
|
|
|
|
default=self.fields
|
|
|
|
|
)
|
|
|
|
|
if self.command is None:
|
|
|
|
|
self.err('`command` field must be string or list')
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `command` field')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def toTF2(self) -> str:
|
|
|
|
|
if self.alias:
|
|
|
|
|
bindOrAlias = 'alias'
|
|
|
|
@ -167,11 +177,8 @@ class Impulse(Bind):
|
|
|
|
|
'secondary': 'slot2',
|
|
|
|
|
'melee': 'slot3'
|
|
|
|
|
}
|
|
|
|
|
try:
|
|
|
|
|
cmd = simpleSCs[cmd]
|
|
|
|
|
except KeyError:
|
|
|
|
|
# not a shortcut
|
|
|
|
|
pass
|
|
|
|
|
# if is shortcut, change
|
|
|
|
|
cmd = simpleSCs.get(cmd, cmd)
|
|
|
|
|
|
|
|
|
|
if cmd == 'voice':
|
|
|
|
|
cmd = 'voicemenu'
|
|
|
|
@ -183,8 +190,8 @@ class Impulse(Bind):
|
|
|
|
|
elif cmd == 'loadout' and restOfCmd.isalpha():
|
|
|
|
|
cmd = 'load_itempreset'
|
|
|
|
|
try:
|
|
|
|
|
restOfCmd = restOfCmd.lower()
|
|
|
|
|
restOfCmd = str(['a','b','c','d'].index(restOfCmd))
|
|
|
|
|
loadoutNum = ['a','b','c','d'].index(restOfCmd.lower())
|
|
|
|
|
restOfCmd = str(loadoutNum)
|
|
|
|
|
except ValueError:
|
|
|
|
|
# not a load_itempreset shortcut
|
|
|
|
|
pass
|
|
|
|
@ -204,9 +211,9 @@ class Impulse(Bind):
|
|
|
|
|
keyword = keyword.lower()
|
|
|
|
|
|
|
|
|
|
allLists = (
|
|
|
|
|
('medic', 'thanks', 'go', 'move up', 'go left', 'go right', 'yes', 'no', 'pass to me'),
|
|
|
|
|
('incoming', 'spy', 'sentry ahead', 'teleporter here', 'dispenser here', 'sentry here', 'activate uber', 'uber ready'),
|
|
|
|
|
('help', 'battle cry', 'cheers', 'jeers', 'positive', 'negative', 'nice shot', 'good job'),
|
|
|
|
|
('medic', 'thanks', 'go', 'move up', 'go left', 'go right', 'yes', 'no', 'pass to me'),
|
|
|
|
|
('incoming', 'spy', 'sentry ahead', 'teleporter here', 'dispenser here', 'sentry here', 'activate uber', 'uber ready'),
|
|
|
|
|
('help', 'battle cry', 'cheers', 'jeers', 'positive', 'negative', 'nice shot', 'good job'),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for menu, voiceList in enumerate(allLists):
|
|
|
|
@ -221,40 +228,32 @@ class Impulse(Bind):
|
|
|
|
|
'exit': '1 1',
|
|
|
|
|
'sentry': '2 0'
|
|
|
|
|
}
|
|
|
|
|
for shortBuild, num in buildingNums.items():
|
|
|
|
|
if building == shortBuild:
|
|
|
|
|
return num
|
|
|
|
|
return buildingNums.get(building, building)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Hold(Bind):
|
|
|
|
|
def verify(self):
|
|
|
|
|
self.press = None
|
|
|
|
|
self.release = None
|
|
|
|
|
self.press: list = None
|
|
|
|
|
self.release: list = None
|
|
|
|
|
if not isinstance(self.fields, dict):
|
|
|
|
|
self.fields = {'press': self.fields}
|
|
|
|
|
|
|
|
|
|
# verify press
|
|
|
|
|
try:
|
|
|
|
|
self.press = self.fields.pop('press')
|
|
|
|
|
if isinstance(self.press, str):
|
|
|
|
|
self.press = self.press.split(';')
|
|
|
|
|
elif not isinstance(self.press, list):
|
|
|
|
|
self.press = self.cmdListFrom('press')
|
|
|
|
|
if self.press is None:
|
|
|
|
|
self.err('`press` field must be string or list')
|
|
|
|
|
self.press = None
|
|
|
|
|
except KeyError:
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `press` field')
|
|
|
|
|
if self.press is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# verify release
|
|
|
|
|
try:
|
|
|
|
|
self.release = self.fields.pop('release')
|
|
|
|
|
if isinstance(self.release, str):
|
|
|
|
|
self.release = self.release.split(';')
|
|
|
|
|
elif not isinstance(self.release, list):
|
|
|
|
|
self.release = self.cmdListFrom('release')
|
|
|
|
|
if self.release is None:
|
|
|
|
|
self.err('`release` field must be string or list')
|
|
|
|
|
self.release = None
|
|
|
|
|
except popErrors:
|
|
|
|
|
if self.press is None:
|
|
|
|
|
return
|
|
|
|
|
self.warn('has no `release`, creating one')
|
|
|
|
|
# no release specified, do -action for each item in press
|
|
|
|
|
self.release = []
|
|
|
|
@ -271,11 +270,11 @@ class Hold(Bind):
|
|
|
|
|
|
|
|
|
|
# Making impulse instances from self.press and .release
|
|
|
|
|
# allows them to share the shortcuts
|
|
|
|
|
pressObj = Impulse(f'+{holdStr}', self.press)
|
|
|
|
|
pressObj = Impulse('+' + holdStr, self.press)
|
|
|
|
|
pressObj.alias = True
|
|
|
|
|
pressStr = pressObj.toTF2()
|
|
|
|
|
|
|
|
|
|
releaseObj = Impulse(f'-{holdStr}', self.release)
|
|
|
|
|
releaseObj = Impulse('-' + holdStr, self.release)
|
|
|
|
|
releaseObj.alias = True
|
|
|
|
|
releaseStr = releaseObj.toTF2()
|
|
|
|
|
|
|
|
|
@ -296,34 +295,29 @@ class Hold(Bind):
|
|
|
|
|
|
|
|
|
|
class Toggle(Bind):
|
|
|
|
|
def verify(self):
|
|
|
|
|
self.on = None
|
|
|
|
|
self.off = None
|
|
|
|
|
self.on : list = None
|
|
|
|
|
self.off: list = None
|
|
|
|
|
if not isinstance(self.fields, dict):
|
|
|
|
|
self.fields = {'on': self.fields}
|
|
|
|
|
|
|
|
|
|
# verify on
|
|
|
|
|
try:
|
|
|
|
|
self.on = self.fields.pop('on')
|
|
|
|
|
if isinstance(self.on, str):
|
|
|
|
|
self.on = self.on.split(';')
|
|
|
|
|
elif not isinstance(self.on, list):
|
|
|
|
|
self.err('`on` field must be string or list')
|
|
|
|
|
self.on = None
|
|
|
|
|
except KeyError:
|
|
|
|
|
self.on = self.cmdListFrom('on')
|
|
|
|
|
if self.on is None:
|
|
|
|
|
self.err(f'`on` field must be string or list')
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `on` field')
|
|
|
|
|
if self.on is None:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# verify off
|
|
|
|
|
try:
|
|
|
|
|
self.off = self.fields.pop('off')
|
|
|
|
|
if isinstance(self.off, str):
|
|
|
|
|
self.off = self.off.split(';')
|
|
|
|
|
elif not isinstance(self.off, list):
|
|
|
|
|
self.err('`off` field must be string or list')
|
|
|
|
|
self.off = self.cmdListFrom('off')
|
|
|
|
|
if self.off is None:
|
|
|
|
|
self.err(f'`off` field must be string or list')
|
|
|
|
|
except popErrors:
|
|
|
|
|
# no off specified, do -action for each item in on
|
|
|
|
|
self.off = []
|
|
|
|
|
if self.on is None:
|
|
|
|
|
return
|
|
|
|
|
for cmd in self.on:
|
|
|
|
|
if cmd[0] == '+':
|
|
|
|
|
self.off.append('-' + cmd[1:])
|
|
|
|
@ -357,25 +351,20 @@ class Toggle(Bind):
|
|
|
|
|
|
|
|
|
|
class Double(Bind):
|
|
|
|
|
defaultDict = {}
|
|
|
|
|
condDict = {}
|
|
|
|
|
bindNames = []
|
|
|
|
|
condDict = {}
|
|
|
|
|
bindNames = []
|
|
|
|
|
|
|
|
|
|
def verify(self):
|
|
|
|
|
self.primary = None
|
|
|
|
|
self.primStr = f'{self.key}_primary'
|
|
|
|
|
|
|
|
|
|
self.secondary = None
|
|
|
|
|
self.secondStr = f'{self.key}_secondary'
|
|
|
|
|
|
|
|
|
|
self.condition = None
|
|
|
|
|
self.isToggle = False
|
|
|
|
|
|
|
|
|
|
# name of a bind type
|
|
|
|
|
self.type = None
|
|
|
|
|
|
|
|
|
|
# either 'released' (default) or 'both'
|
|
|
|
|
self.primStr = f'{self.key}_primary'
|
|
|
|
|
self.secondStr = f'{self.key}_secondary'
|
|
|
|
|
self.isToggle = False
|
|
|
|
|
self.cancelBoth = False
|
|
|
|
|
|
|
|
|
|
self.primary: Bind = None
|
|
|
|
|
self.secondary: Bind = None
|
|
|
|
|
self.condition: str = None
|
|
|
|
|
self.type: str = None
|
|
|
|
|
|
|
|
|
|
# toggler
|
|
|
|
|
try:
|
|
|
|
|
self.condition = self.fields.pop('condition')
|
|
|
|
@ -384,15 +373,12 @@ class Double(Bind):
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `condition` field')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.isToggle = self.fields.pop('toggle')
|
|
|
|
|
if not isinstance(self.isToggle, bool):
|
|
|
|
|
self.err(
|
|
|
|
|
'`toggle` field should be "yes" or "no", '
|
|
|
|
|
+ f'not "{self.isToggle}"'
|
|
|
|
|
)
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.isToggle = False
|
|
|
|
|
self.isToggle = self.optional('toggle', default=False)
|
|
|
|
|
if not isinstance(self.isToggle, bool):
|
|
|
|
|
self.err(
|
|
|
|
|
'`toggle` field should be "yes" or "no",'
|
|
|
|
|
+ f' not "{self.isToggle}"'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# type
|
|
|
|
|
try:
|
|
|
|
@ -407,56 +393,57 @@ class Double(Bind):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# cancel mode, must happend after type has been inferred
|
|
|
|
|
try:
|
|
|
|
|
cancel = self.fields.pop('cancel')
|
|
|
|
|
cancel = self.optional('cancel', default='released')
|
|
|
|
|
|
|
|
|
|
if not isinstance(cancel, str):
|
|
|
|
|
self.err(f'`cancel` field must be "released" or "both"')
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
if cancel == 'both':
|
|
|
|
|
if self.type == 'hold':
|
|
|
|
|
self.cancelBoth = True
|
|
|
|
|
else:
|
|
|
|
|
self.err(
|
|
|
|
|
'`cancel` field only affects "hold", '
|
|
|
|
|
+ f'not "{self.type}"'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
elif cancel != 'released':
|
|
|
|
|
if not isinstance(cancel, str):
|
|
|
|
|
self.err(f'`cancel` field must be "released" or "both"')
|
|
|
|
|
else:
|
|
|
|
|
if cancel == 'both':
|
|
|
|
|
if self.type == 'hold':
|
|
|
|
|
self.cancelBoth = True
|
|
|
|
|
else:
|
|
|
|
|
self.err(
|
|
|
|
|
'`cancel` field must be "released" '
|
|
|
|
|
+ f'or "both", not "{cancel}"'
|
|
|
|
|
'`cancel` field only affects "hold",'
|
|
|
|
|
+ f' not "{self.type}"'
|
|
|
|
|
)
|
|
|
|
|
except popErrors:
|
|
|
|
|
cancel = 'released'
|
|
|
|
|
elif cancel == 'released':
|
|
|
|
|
self.cancelBoth = False
|
|
|
|
|
else:
|
|
|
|
|
self.err(
|
|
|
|
|
'`cancel` field must be "released"'
|
|
|
|
|
+ f' or "both", not "{cancel}"'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# primary action
|
|
|
|
|
try:
|
|
|
|
|
mainSection = self.fields.pop('primary')
|
|
|
|
|
mainBind = Bind(f'{self.type} {self.primStr}', mainSection)
|
|
|
|
|
mainBind = mainBind.toTargetType()
|
|
|
|
|
|
|
|
|
|
self.errors.extend(mainBind.errors)
|
|
|
|
|
self.warnings.extend(mainBind.warnings)
|
|
|
|
|
self.errors.remove(f'invalid key name: "{self.primStr}"')
|
|
|
|
|
self.primary = mainBind
|
|
|
|
|
self.primary = self.getSection('primary', self.primStr)
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `primary` field')
|
|
|
|
|
self.primary = None
|
|
|
|
|
|
|
|
|
|
# secondary action
|
|
|
|
|
try:
|
|
|
|
|
altSection = self.fields.pop('secondary')
|
|
|
|
|
altBind = Bind(f'{self.type} {self.secondStr}', altSection)
|
|
|
|
|
altBind = altBind.toTargetType()
|
|
|
|
|
|
|
|
|
|
self.errors.extend(altBind.errors)
|
|
|
|
|
self.warnings.extend(altBind.warnings)
|
|
|
|
|
self.errors.remove(f'invalid key name: "{self.secondStr}"')
|
|
|
|
|
self.secondary = altBind
|
|
|
|
|
self.secondary = self.getSection('secondary', self.secondStr)
|
|
|
|
|
except popErrors:
|
|
|
|
|
self.err('requires `secondary` field')
|
|
|
|
|
self.secondary = None
|
|
|
|
|
|
|
|
|
|
if self.primary is self.secondary is None:
|
|
|
|
|
self.err('has neither primary nor secondary')
|
|
|
|
|
|
|
|
|
|
def getSection(self, popName, key, /) -> Bind:
|
|
|
|
|
section = self.fields.pop(popName)
|
|
|
|
|
bind = Bind(f'{self.type} {key}', section)
|
|
|
|
|
bind = bind.toTargetType()
|
|
|
|
|
|
|
|
|
|
bind.errors.remove(f'invalid key name: "{key}"')
|
|
|
|
|
self.prettifyList(bind.errors, key)
|
|
|
|
|
self.errors.extend(bind.errors)
|
|
|
|
|
self.prettifyList(bind.warnings, key)
|
|
|
|
|
self.warnings.extend(bind.warnings)
|
|
|
|
|
|
|
|
|
|
return bind
|
|
|
|
|
|
|
|
|
|
def prettifyList(self, strList, origStr):
|
|
|
|
|
repStr = ' '.join(origStr.split('_', 1))
|
|
|
|
|
for i, cmd in enumerate(strList):
|
|
|
|
|
strList[i] = cmd.replace(origStr, repStr)
|
|
|
|
|
|
|
|
|
|
def toTF2(self) -> str:
|
|
|
|
|
# Get code for primary and secondary actions.
|
|
|
|
|