got to where i was at the start of the refactor
parent
3f9f35cced
commit
5014161b02
|
@ -18,19 +18,13 @@ validKeyList = [
|
|||
'leftarrow', 'rightarrow'
|
||||
]
|
||||
|
||||
bindTypes = [
|
||||
'impulse',
|
||||
'hold',
|
||||
'toggle',
|
||||
'double',
|
||||
'repeat'
|
||||
]
|
||||
|
||||
class bind:
|
||||
class bind(object):
|
||||
'''
|
||||
Parent class for all bind types.
|
||||
Verifies key, creates local variables
|
||||
'''
|
||||
bindTypes = []
|
||||
instances = {}
|
||||
|
||||
def __init__(self, key, fields):
|
||||
self.alias = False
|
||||
|
@ -43,19 +37,29 @@ class bind:
|
|||
# and some other universal fields like alias and finds targetType
|
||||
self.verify()
|
||||
|
||||
if type(self) == bind or len(self.fields) == 0:
|
||||
if type(self) is bind:
|
||||
# not using isinstance(), because all subclasses are also instances
|
||||
# of bind.
|
||||
return
|
||||
# verify function should remove all fields relavent to the bind.
|
||||
# Any extras are errors
|
||||
|
||||
self.errors.append(f'extra fields in "{self.key}":')
|
||||
if isinstance(self.fields, str):
|
||||
# iterating over a str returns each character,
|
||||
# making meaningless error messages
|
||||
self.errors.append(f' "{self.fields}"')
|
||||
else:
|
||||
for field in self.fields:
|
||||
self.errors.append(f' "{field}"')
|
||||
if len(self.fields) > 0:
|
||||
# verify function should remove all fields relavent to the bind.
|
||||
# Any extras are errors
|
||||
|
||||
self.errors.append(f'extra fields in "{self.key}":')
|
||||
if isinstance(self.fields, str):
|
||||
# iterating over a str returns each character,
|
||||
# making meaningless error messages
|
||||
self.errors.append(f' "{self.fields}"')
|
||||
else:
|
||||
for field in self.fields:
|
||||
self.errors.append(f' "{field}"')
|
||||
elif len(self.errors) == 0:
|
||||
# no errors, add new instance to the list of instances
|
||||
try:
|
||||
self.instances[type(self)].append(self)
|
||||
except KeyError:
|
||||
self.instances[type(self)] = [self]
|
||||
|
||||
def verify(self):
|
||||
|
||||
|
@ -63,14 +67,14 @@ class bind:
|
|||
typeName, self.key = self.key.split(' ', 1)
|
||||
self.key = self.key.lower()
|
||||
except ValueError:
|
||||
# catastrophic error: no type
|
||||
# catastrophic error: either no type or no key, assume no type
|
||||
self.errors.append(f'could not find type in "{self.key}"')
|
||||
return
|
||||
|
||||
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.errors.append(f'alias should be "yes" or "no", not "{self.alias}"')
|
||||
self.alias = False
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
self.alias = False
|
||||
|
@ -78,16 +82,9 @@ class bind:
|
|||
if (not self.alias) and (self.key not in validKeyList):
|
||||
self.errors.append(f'invalid key name: "{self.key}"')
|
||||
|
||||
types = {
|
||||
'impulse': impulse,
|
||||
'hold': hold,
|
||||
'toggle': toggle,
|
||||
'double': double,
|
||||
'repeat': repeat
|
||||
}
|
||||
for loopName, typeClass in types.items():
|
||||
if loopName == typeName:
|
||||
self.targetType = typeClass
|
||||
for type_ in self.bindTypes:
|
||||
if typeName == type_.__name__:
|
||||
self.targetType = type_
|
||||
break
|
||||
|
||||
if self.targetType is None:
|
||||
|
@ -101,6 +98,7 @@ class bind:
|
|||
# cast to targetType, extend errors
|
||||
bind = self.targetType(self.key, self.fields)
|
||||
bind.errors.extend(self.errors)
|
||||
bind.alias = self.alias
|
||||
return bind
|
||||
|
||||
def err(self, message):
|
||||
|
@ -110,18 +108,19 @@ class bind:
|
|||
class impulse(bind):
|
||||
def verify(self):
|
||||
self.command = None
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'command': self.fields}
|
||||
|
||||
try:
|
||||
self.command = self.fields.pop('command')
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
self.fields = {'command': self.fields}
|
||||
self.command = self.fields.pop('command')
|
||||
except KeyError:
|
||||
self.err('requires `command` field')
|
||||
|
||||
if isinstance(self.command, str):
|
||||
self.command = self.command.split(';')
|
||||
|
||||
elif not isinstance(self.command, list):
|
||||
self.err('must be command or argument of string or list')
|
||||
self.err('`command` field must be argument of string or list')
|
||||
self.command = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
|
@ -144,9 +143,9 @@ class impulse(bind):
|
|||
cmd, restOfCmd = instruction, ''
|
||||
|
||||
simpleSCs = {
|
||||
"primary": "slot1",
|
||||
"secondary": "slot2",
|
||||
"melee": "slot3"
|
||||
'primary': 'slot1',
|
||||
'secondary': 'slot2',
|
||||
'melee': 'slot3'
|
||||
}
|
||||
try:
|
||||
cmd = simpleSCs[cmd]
|
||||
|
@ -154,14 +153,14 @@ class impulse(bind):
|
|||
# not a shortcut
|
||||
pass
|
||||
|
||||
if cmd == "voice":
|
||||
cmd = "voicemenu"
|
||||
if cmd == 'voice':
|
||||
cmd = 'voicemenu'
|
||||
restOfCmd = self.expandVoice(restOfCmd)
|
||||
|
||||
elif cmd == "build" or cmd == "destroy":
|
||||
elif cmd == 'build' or cmd == 'destroy':
|
||||
restOfCmd = self.expandBuildings(restOfCmd)
|
||||
|
||||
elif cmd == "load_itempreset" and restOfCmd.isalpha():
|
||||
elif cmd == 'load_itempreset' and restOfCmd.isalpha():
|
||||
try:
|
||||
restOfCmd = restOfCmd.lower()
|
||||
restOfCmd = ['a','b','c','d'].index(restOfCmd)
|
||||
|
@ -169,7 +168,7 @@ class impulse(bind):
|
|||
# not a load_itempreset shortcut
|
||||
pass
|
||||
|
||||
if restOfCmd != "":
|
||||
if restOfCmd != '':
|
||||
cmd += ' ' + restOfCmd
|
||||
instList[i] = cmd
|
||||
|
||||
|
@ -179,9 +178,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):
|
||||
|
@ -191,10 +190,10 @@ class impulse(bind):
|
|||
|
||||
def expandBuildings(self, building):
|
||||
buildingNums = {
|
||||
"dispenser": "0 0",
|
||||
"entrance": "1 0",
|
||||
"exit": "1 1",
|
||||
"sentry": "2 0"
|
||||
'dispenser': '0 0',
|
||||
'entrance': '1 0',
|
||||
'exit': '1 1',
|
||||
'sentry': '2 0'
|
||||
}
|
||||
for shortBuild, num in buildingNums.items():
|
||||
if building == shortBuild:
|
||||
|
@ -205,60 +204,122 @@ class hold(bind):
|
|||
def verify(self):
|
||||
self.press = None
|
||||
self.release = None
|
||||
if isinstance(self.fields, dict):
|
||||
# verify press
|
||||
try:
|
||||
self.press = self.fields.pop('press')
|
||||
if not isinstance(self.press, (str, list)):
|
||||
self.err('press must be string or list')
|
||||
self.press = None
|
||||
except KeyError:
|
||||
self.err('requires press field')
|
||||
|
||||
# verify release
|
||||
try:
|
||||
self.release = self.fields.pop('release')
|
||||
if not isinstance(self.release, (str, list)):
|
||||
self.err('release must be string or list')
|
||||
self.release = None
|
||||
except KeyError:
|
||||
self.err('requires release field')
|
||||
|
||||
elif isinstance(self.fields, str):
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'press': self.fields}
|
||||
|
||||
# verify press
|
||||
try:
|
||||
self.press = self.fields.pop('press')
|
||||
except KeyError:
|
||||
self.err('requires `press` field')
|
||||
|
||||
if isinstance(self.press, str):
|
||||
self.press = self.press.split(';')
|
||||
elif not isinstance(self.press, list):
|
||||
self.err('`press` field must be string or list')
|
||||
self.press = None
|
||||
|
||||
# verify release
|
||||
try:
|
||||
self.release = self.fields.pop('release')
|
||||
except KeyError:
|
||||
# no release specified, do -action for each item in press
|
||||
self.release = []
|
||||
for cmd in self.press:
|
||||
if cmd[0] == '+':
|
||||
self.release.append('-' + cmd[1:])
|
||||
|
||||
if isinstance(self.release, str):
|
||||
self.release = self.release.split(';')
|
||||
elif not isinstance(self.release, list):
|
||||
self.err('"release" field must be string or list')
|
||||
self.release = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
self.err('must be press and release, or argument of string')
|
||||
bindOrAlias = 'bind'
|
||||
holdStr = f'hold_{self.key}'
|
||||
|
||||
# Making impulse instances from self.press and .release
|
||||
# allows them to share the shortcuts
|
||||
pressObj = impulse(f'+{holdStr}', self.press)
|
||||
pressObj.alias = True
|
||||
pressStr = pressObj.toTF2()
|
||||
|
||||
releaseObj = impulse(f'-{holdStr}', self.release)
|
||||
releaseObj.alias = True
|
||||
releaseStr = releaseObj.toTF2()
|
||||
|
||||
if self.alias:
|
||||
# if alias, do this to avoid activating
|
||||
# and never deactivating
|
||||
self.key = '+' + self.key
|
||||
|
||||
return pressStr + releaseStr + f'{bindOrAlias} {self.key} "+{holdStr}"\n'
|
||||
|
||||
|
||||
class toggle(bind):
|
||||
def verify(self):
|
||||
self.on = None
|
||||
self.off = None
|
||||
if isinstance(self.fields, dict):
|
||||
# verify on
|
||||
try:
|
||||
self.on = self.fields.pop('on')
|
||||
if not isinstance(self.on, (str, list)):
|
||||
self.err('on must be string or list')
|
||||
self.on = None
|
||||
except KeyError:
|
||||
self.err('requires on field')
|
||||
|
||||
# verify off
|
||||
try:
|
||||
self.off = self.fields.pop('off')
|
||||
if not isinstance(self.off, (str, list)):
|
||||
self.err('off must be string or list')
|
||||
self.off = None
|
||||
except KeyError:
|
||||
self.err('requires end field')
|
||||
|
||||
elif isinstance(self.fields, str):
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'on': self.fields}
|
||||
|
||||
# verify on
|
||||
try:
|
||||
self.on = self.fields.pop('on')
|
||||
except KeyError:
|
||||
self.err('requires `on` field')
|
||||
|
||||
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
|
||||
|
||||
# verify off
|
||||
try:
|
||||
self.off = self.fields.pop('off')
|
||||
except KeyError:
|
||||
# no off specified, do -action for each item in on
|
||||
self.off = []
|
||||
for cmd in self.on:
|
||||
if cmd[0] == '+':
|
||||
self.off.append('-' + cmd[1:])
|
||||
|
||||
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 = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
self.err('must be on and end, or argument of string')
|
||||
bindOrAlias = 'bind'
|
||||
toggleStr = f'toggle_{self.key}'
|
||||
onStr = f'{toggleStr}_on'
|
||||
offStr = f'{toggleStr}_off'
|
||||
|
||||
onObj = impulse(onStr, self.on)
|
||||
onObj.alias = True
|
||||
toggleOn = onObj.toTF2()[
|
||||
# remove trailing " and \n
|
||||
:-2
|
||||
]
|
||||
|
||||
offObj = impulse(offStr, self.off)
|
||||
offObj.alias = True
|
||||
toggleOff = offObj.toTF2()[:-2]
|
||||
|
||||
return \
|
||||
f'{toggleOn}; alias {toggleStr} {offStr}"\n' +\
|
||||
f'{toggleOff}; alias {toggleStr} {onStr}"\n' +\
|
||||
f'alias {toggleStr} "{onStr}"\n' +\
|
||||
f'{bindOrAlias} {self.key} "{toggleStr}"\n'
|
||||
|
||||
|
||||
class double(bind):
|
||||
|
@ -267,69 +328,142 @@ class double(bind):
|
|||
|
||||
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"
|
||||
# either 'released' (default) or 'both'
|
||||
self.cancel = 'released'
|
||||
|
||||
# toggler
|
||||
try:
|
||||
self.condition = self.fields.pop('condition')
|
||||
if self.condition not in validKeyList:
|
||||
self.err(f'has invalid condition to toggle: "{self.condition}"')
|
||||
self.err(f'has invalid `condition` field: "{self.condition}"')
|
||||
except KeyError:
|
||||
self.err('requires condition to toggle')
|
||||
self.err('requires `condition` field')
|
||||
|
||||
# cancel mode
|
||||
try:
|
||||
self.cancel = self.fields.pop('cancel')
|
||||
if self.cancel not in ['released', 'both']:
|
||||
self.err(f'cancel must be either "released" or "both", not "{self.cancel}"')
|
||||
except KeyError:
|
||||
# maintain default
|
||||
pass
|
||||
if 'toggle' in self.fields:
|
||||
self.isToggle = self.fields.pop('toggle')
|
||||
if not isinstance(self.isToggle, bool):
|
||||
self.err(f'`toggle` field should be "yes" or "no", not "{self.isToggle}"')
|
||||
|
||||
# type
|
||||
try:
|
||||
self.type = self.fields.pop('type')
|
||||
if self.type not in bindTypes:
|
||||
if self.type not in [ type_.__name__ for type_ in self.bindTypes ]:
|
||||
# catastrophic: invalid type
|
||||
self.err(f'has invalid type: "{self.type}"')
|
||||
return
|
||||
except KeyError:
|
||||
# catastrophic: no type given
|
||||
self.err('requires type')
|
||||
self.err('requires `type` field')
|
||||
return
|
||||
|
||||
# cancel mode
|
||||
if 'cancel' in self.fields:
|
||||
self.cancel = self.fields.pop('cancel')
|
||||
if self.cancel in ('released', 'both'):
|
||||
if self.cancel == 'both' and self.type != 'hold':
|
||||
self.err(f'`cancel` field only affects "hold", not "{self.type}"')
|
||||
elif isinstance(self.cancel, str):
|
||||
self.err(f'`cancel` field must be "released" or "both", not "{self.cancel}"')
|
||||
else:
|
||||
self.err(f'`cancel` field must be argument of "released" or "both"')
|
||||
|
||||
# primary action
|
||||
try:
|
||||
mainSection = self.fields.pop('primary')
|
||||
# mainSection.update({'alias': True})
|
||||
|
||||
mainAction = bind(f'{self.type} primary', mainSection)
|
||||
if mainAction.targetType is not None:
|
||||
mainAction = mainAction.targetType(mainAction.key, mainAction.fields)
|
||||
|
||||
self.errors.extend(mainAction.errors)
|
||||
mainAction = bind(f'{self.type} {self.primStr}', mainSection)
|
||||
self.primary = mainAction.toTargetType()
|
||||
except KeyError:
|
||||
self.err('requires primary action')
|
||||
self.err('requires `primary` field')
|
||||
|
||||
# secondary action
|
||||
try:
|
||||
altSection = self.fields.pop('secondary')
|
||||
# altSection.update({'alias': True})
|
||||
|
||||
altBind = bind(f'{self.type} secondary', altSection)
|
||||
if altBind.targetType is not None:
|
||||
altBind = altBind.targetType(altBind.key, altBind.fields)
|
||||
|
||||
self.errors.extend(altBind.errors)
|
||||
altBind = bind(f'{self.type} {self.secondStr}', altSection)
|
||||
self.secondary = altBind.toTargetType()
|
||||
except KeyError:
|
||||
self.err('requires secondary action')
|
||||
self.err('requires `secondary` field')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
bindOrAlias = 'bind'
|
||||
|
||||
# Get code for primary and secondary actions
|
||||
self.primary.alias = True
|
||||
mainCode = self.primary.toTF2()
|
||||
self.secondary.alias = True
|
||||
altCode = self.secondary.toTF2()
|
||||
|
||||
# Make code to toggle between the two actions
|
||||
pShiftStr = f'+shift_{self.key}'
|
||||
mShiftStr = f'-shift_{self.key}'
|
||||
|
||||
if self.cancel == 'both':
|
||||
# code to cancel both if either is released
|
||||
# it copies the - statement from each to both.
|
||||
# if it just extracted the name of the - statement,
|
||||
# you'd end up with each recursively calling the other
|
||||
|
||||
mainLines = mainCode.splitlines()
|
||||
mainMinusLine = mainLines[1]
|
||||
# second arg, without first quote
|
||||
mainMinusStr = mainMinusLine.split(' ', 2)[2][1:]
|
||||
|
||||
altLines = altCode.splitlines()
|
||||
altMinusLine = altLines[1]
|
||||
# same as above
|
||||
altMinusStr = altMinusLine.split(' ', 2)[2][1:]
|
||||
|
||||
mainLines[1] = mainLines[1][:-1] + f';{altMinusStr}'
|
||||
altLines[1] = altLines[1][:-1] + f';{mainMinusStr}'
|
||||
mainCode = '\n'.join(mainLines) + '\n'
|
||||
altCode = '\n'.join(altLines) + '\n'
|
||||
|
||||
if self.type == 'hold':
|
||||
self.primStr = '+' + self.primStr
|
||||
self.secondStr = '+' + self.secondStr
|
||||
|
||||
result = mainCode + altCode +\
|
||||
f'alias {pShiftStr} "{bindOrAlias} {self.key} {self.primStr}"\n' +\
|
||||
f'alias {mShiftStr} "{bindOrAlias} {self.key} {self.secondStr}"\n'+\
|
||||
f'{bindOrAlias} {self.key} "{self.primStr}"\n'
|
||||
|
||||
try:
|
||||
# If the condition key (like 'mouse4') already has toggles,
|
||||
# just append another toggle string
|
||||
changes = self.condDict[self.condition]['change_keys']
|
||||
restores = self.condDict[self.condition]['restore_keys']
|
||||
|
||||
if pShiftStr not in changes:
|
||||
# not already in changes
|
||||
changes.append(pShiftStr)
|
||||
restores.append(mShiftStr)
|
||||
|
||||
except KeyError:
|
||||
# If the condition key doesn't already exist, make it
|
||||
self.condDict.update( {
|
||||
self.condition: {
|
||||
'change_keys': [ pShiftStr ],
|
||||
'restore_keys': [ mShiftStr ],
|
||||
'alias': self.alias
|
||||
}
|
||||
} )
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class repeat(bind):
|
||||
|
@ -354,3 +488,12 @@ class repeat(bind):
|
|||
self.command = None
|
||||
except KeyError:
|
||||
self.err('requires command')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
# commented-out placeholder
|
||||
return f'// repeat {self.key}\n'
|
||||
|
||||
# This is at the bottom because it has to happen after
|
||||
# all inheritances have been completed
|
||||
|
||||
bind.bindTypes = bind.__subclasses__()
|
Loading…
Reference in New Issue