Compare commits
14 Commits
class_base
...
main
Author | SHA1 | Date |
---|---|---|
Nicholas Hope | 41273df385 | |
Nicholas Hope | 15330c6901 | |
Nicholas Hope | db2d8c5e37 | |
Nicholas Hope | a222ebd63e | |
Nicholas Hope | 017c45eacf | |
Nicholas Hope | 683ca64c43 | |
Nicholas Hope | 1d12ae88c1 | |
Nicholas Hope | 4296cc0748 | |
Nicholas Hope | 828f35d133 | |
Nicholas Hope | 15af6e1cb6 | |
Nicholas Hope | 9331f13786 | |
Nicholas Hope | 607894648c | |
Nicholas Hope | b4506bda77 | |
Nicholas Hope | f9c7aa403e |
|
@ -29,8 +29,19 @@ default:
|
|||
condition: mouse4
|
||||
cancel: both
|
||||
type: hold
|
||||
primary: +class_action
|
||||
secondary: +reload
|
||||
primary: +reload
|
||||
secondary: +class_action
|
||||
# class action is something useful for each class,
|
||||
# like destroying and rebuilding a sentry for the engineer
|
||||
# this is just the default. I do a lot of hybridknight and
|
||||
# mantread soldier so having a melee/main weapon switch bind
|
||||
# is useful
|
||||
hold class_action:
|
||||
alias: yes
|
||||
press:
|
||||
- "slot3"
|
||||
- "wait 10"
|
||||
- "slot1"
|
||||
|
||||
# other
|
||||
impulse =: kill
|
||||
|
@ -54,6 +65,10 @@ default:
|
|||
# toggle
|
||||
toggle capslock: +voicerecord
|
||||
|
||||
hold space:
|
||||
- "+duck"
|
||||
- "+jump"
|
||||
|
||||
# hold: null-cancelled movement, so hitting a while holding d causes
|
||||
# me to go left instead of stopping, or vice-versa.
|
||||
hold a:
|
||||
|
@ -74,6 +89,11 @@ default:
|
|||
- "-moveright"
|
||||
- "maybe_move_left"
|
||||
- "unalias maybe_move_right"
|
||||
|
||||
# both do something different for engi
|
||||
bind mouse5: +jump
|
||||
bind 4: slot4
|
||||
|
||||
# This just stops an error message the first time you release
|
||||
# either of 'a' or 'd'
|
||||
impulse maybe_move_left:
|
||||
|
@ -82,18 +102,6 @@ default:
|
|||
impulse maybe_move_right:
|
||||
alias: yes
|
||||
command: ""
|
||||
# class action is something useful for each class,
|
||||
# like destroying and rebuilding a sentry for the engineer
|
||||
# this is just the default. I do a lot of hybridknight and
|
||||
# mantread soldier so having a melee/main weapon switch bind
|
||||
# is useful
|
||||
hold class_action:
|
||||
alias: yes
|
||||
press:
|
||||
- "slot3"
|
||||
- "wait 10"
|
||||
- "slot1"
|
||||
release: ""
|
||||
|
||||
impulse load0:
|
||||
alias: yes
|
||||
|
@ -122,7 +130,7 @@ default:
|
|||
- "alias reload_presets load3"
|
||||
literal set_default_loadout:
|
||||
alias reload_presets load0
|
||||
impulse backspace:
|
||||
impulse alt:
|
||||
reload_presets
|
||||
|
||||
engi:
|
||||
|
@ -131,6 +139,32 @@ engi:
|
|||
press:
|
||||
- destroy sentry
|
||||
- build sentry
|
||||
double 4:
|
||||
type: double
|
||||
condition: mouse4
|
||||
|
||||
primary:
|
||||
type: impulse
|
||||
condition: mouse5
|
||||
primary:
|
||||
- destroy dispenser
|
||||
- build dispenser
|
||||
secondary:
|
||||
- destroy exit
|
||||
- build exit
|
||||
secondary:
|
||||
type: impulse
|
||||
condition: mouse5
|
||||
primary:
|
||||
- destroy entrance
|
||||
- build entrance
|
||||
secondary:
|
||||
- destroy entrance
|
||||
- build entrance
|
||||
|
||||
soldier:
|
||||
# I want control over how high I jump
|
||||
bind space: +jump
|
||||
|
||||
medic:
|
||||
# "Radar" feature: causes all teammates to autocall for medic, allowing
|
||||
|
@ -144,21 +178,3 @@ medic:
|
|||
condition: mouse4
|
||||
primary: voice medic
|
||||
secondary: voice uber ready
|
||||
|
||||
soldier:
|
||||
double mouse1:
|
||||
type: hold
|
||||
condition: mouse4
|
||||
cancel: both
|
||||
# normal firing
|
||||
primary: +attack
|
||||
# rocket jump
|
||||
secondary:
|
||||
press:
|
||||
- +attack
|
||||
- +duck
|
||||
- +jump
|
||||
release:
|
||||
- -attack
|
||||
- -duck
|
||||
- -jump
|
||||
|
|
|
@ -3,4 +3,4 @@ name = tfscript
|
|||
version = 1.0
|
||||
|
||||
[options]
|
||||
packages = src/tfscript
|
||||
packages = tfscript
|
|
@ -91,6 +91,16 @@ def parseCLI():
|
|||
help='Force tfscript to continue until catastrophic failure')
|
||||
parser.add_argument( '-D', '--directory', action='store', type=str,
|
||||
help='Change output directory')
|
||||
# warnings
|
||||
parseWarnNames = [
|
||||
'implicit-release', 'implicit-off',
|
||||
'implicit-primary', 'implicit-secondary',
|
||||
'implicit'
|
||||
]
|
||||
for warnName in parseWarnNames:
|
||||
splitWarnName = ' '.join(warnName.split('-'))
|
||||
parser.add_argument( '-W' + warnName, action='store_true',
|
||||
help=f'Generate warning on {splitWarnName} creation')
|
||||
# positional argument: first non-hyphenated argument is input file
|
||||
parser.add_argument( 'infile', type=argparse.FileType('r'),
|
||||
help='File containing YAML to convert.')
|
||||
|
@ -100,13 +110,13 @@ def getTargetDir(systemName):
|
|||
if systemName == 'Darwin':
|
||||
if float( '.'.join( GetOSRelease().split('.')[0:2] ) ) >= 10.15:
|
||||
warn(
|
||||
'As of macOS Catalina (v10.15), 32-bit applications '
|
||||
'like TF2 do not run. tfscript will run, but you can\'t run TF2 '
|
||||
'on this system',
|
||||
'As of macOS Catalina (v10.15), 32-bit applications'
|
||||
+ ' like TF2 do not run. tfscript will run, but you can\'t run TF2'
|
||||
+ ' on this system',
|
||||
category=RuntimeWarning )
|
||||
return expanduser('~/Library/Application Support/Steam')
|
||||
return expanduser('~/Library/Application Support/Steam')
|
||||
|
||||
elif systemName == 'Windows':
|
||||
if systemName == 'Windows':
|
||||
# oh god why do we have to use the registry
|
||||
accessReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
|
||||
accessKey = OpenKey(accessReg, 'SOFTWARE\\WOW6432Node\\Valve\\Steam')
|
||||
|
@ -114,17 +124,23 @@ def getTargetDir(systemName):
|
|||
while True:
|
||||
try:
|
||||
accessSubkeyName, data, _ = EnumValue(accessKey, keyNum)
|
||||
if accessSubkeyName == 'InstallPath':
|
||||
return data
|
||||
except EnvironmentError:
|
||||
break
|
||||
keyNum += 1
|
||||
else:
|
||||
if accessSubkeyName == 'InstallPath':
|
||||
return data
|
||||
keyNum += 1
|
||||
return None
|
||||
|
||||
elif systemName == 'Linux':
|
||||
return expanduser('~/.local/Steam')
|
||||
if systemName == 'Linux':
|
||||
homedir = expanduser('~')
|
||||
for potentialdir in ['.steam/steam', '.local/share/Steam']:
|
||||
fullTargetPath = normpath(f'{homedir}/{potentialdir}')
|
||||
if isdir(fullTargetPath):
|
||||
return fullTargetPath
|
||||
return None
|
||||
|
||||
elif systemName == 'Java':
|
||||
if systemName == 'Java':
|
||||
warn('Java-based OSes are not supported yet by tfscript.', category=RuntimeWarning)
|
||||
|
||||
return None
|
|
@ -1,6 +1,4 @@
|
|||
import re
|
||||
|
||||
validKeyList = [
|
||||
validKeyList = {
|
||||
# top row
|
||||
'escape', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
|
||||
# keyboard
|
||||
|
@ -18,11 +16,11 @@ validKeyList = [
|
|||
# arrows
|
||||
'uparrow', 'downarrow',
|
||||
'leftarrow', 'rightarrow'
|
||||
]
|
||||
}
|
||||
|
||||
popErrors = (AttributeError, KeyError, TypeError)
|
||||
|
||||
class Bind(object):
|
||||
class ScriptBind(object):
|
||||
'''
|
||||
Parent class for all bind types.
|
||||
Verifies key, creates local variables
|
||||
|
@ -30,26 +28,26 @@ 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
|
||||
# and some other universal fields like alias and finds targetType
|
||||
self.verify()
|
||||
|
||||
if type(self) is Bind:
|
||||
if type(self) is ScriptBind:
|
||||
# not using isinstance(), because all subclasses are also instances
|
||||
# of bind.
|
||||
return
|
||||
|
@ -74,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)
|
||||
|
@ -109,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,23 +137,22 @@ class Bind(object):
|
|||
)
|
||||
|
||||
|
||||
class Impulse(Bind):
|
||||
class Impulse(ScriptBind):
|
||||
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'
|
||||
|
@ -169,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'
|
||||
|
@ -185,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
|
||||
|
@ -206,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):
|
||||
|
@ -223,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):
|
||||
class Hold(ScriptBind):
|
||||
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 = []
|
||||
|
@ -273,55 +270,54 @@ 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()
|
||||
|
||||
if self.alias:
|
||||
# if alias, do this to avoid activating
|
||||
# and never deactivating
|
||||
minuskey = '-' + self.key
|
||||
self.key = '+' + self.key
|
||||
|
||||
return (
|
||||
code = (
|
||||
pressStr + releaseStr
|
||||
+ f'{bindOrAlias} {self.key} "+{holdStr}"\n'
|
||||
)
|
||||
if self.alias:
|
||||
code += f'alias {minuskey} "-{holdStr}"\n'
|
||||
return code
|
||||
|
||||
|
||||
class Toggle(Bind):
|
||||
class Toggle(ScriptBind):
|
||||
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:])
|
||||
|
@ -353,27 +349,22 @@ class Toggle(Bind):
|
|||
)
|
||||
|
||||
|
||||
class Double(Bind):
|
||||
class Double(ScriptBind):
|
||||
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: ScriptBind = None
|
||||
self.secondary: ScriptBind = None
|
||||
self.condition: str = None
|
||||
self.type: str = None
|
||||
|
||||
# toggler
|
||||
try:
|
||||
self.condition = self.fields.pop('condition')
|
||||
|
@ -382,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:
|
||||
|
@ -405,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, /) -> ScriptBind:
|
||||
section = self.fields.pop(popName)
|
||||
bind = ScriptBind(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.
|
||||
|
@ -470,8 +459,8 @@ class Double(Bind):
|
|||
mainCode, altCode = self.getCancelCode(mainCode, altCode)
|
||||
|
||||
if self.type == 'hold':
|
||||
self.primStr = '+' + self.primStr
|
||||
self.secondStr = '+' + self.secondStr
|
||||
self.primStr = '+hold_' + self.primStr
|
||||
self.secondStr = '+hold_' + self.secondStr
|
||||
|
||||
shiftStr = f'shift_{self.key}'
|
||||
shiftCode = self.getChangeCode(shiftStr)
|
||||
|
@ -525,11 +514,13 @@ class Double(Bind):
|
|||
|
||||
mainLines = mainCode.splitlines()
|
||||
mainMinusLine = mainLines[1]
|
||||
mainMinusName = mainMinusLine.split(' ')[1]
|
||||
# second arg, without first or last quote
|
||||
mainMinusStr = mainMinusLine.split(' ', 2)[2][1:-1]
|
||||
|
||||
altLines = altCode.splitlines()
|
||||
altMinusLine = altLines[1]
|
||||
altMinusName = altMinusLine.split(' ')[1]
|
||||
# same as above
|
||||
altMinusStr = altMinusLine.split(' ', 2)[2][1:-1]
|
||||
|
||||
|
@ -539,8 +530,12 @@ class Double(Bind):
|
|||
allCancels = mainMinusSet | altMinusSet
|
||||
allCancelStr = ';'.join(allCancels)
|
||||
|
||||
altLines[1] = altLines[1][:-1] + f';{allCancelStr}"'
|
||||
mainLines[1] = mainLines[1][:-1] + f';{allCancelStr}"'
|
||||
altMinusLineStart = ' '.join(altMinusLine.split(' ')[:2])
|
||||
altLines[1] = altMinusLineStart + f' "{allCancelStr}"'
|
||||
altLines.insert(3, f'alias -{self.secondStr} "{altMinusName}"')
|
||||
mainMinusLineStart = ' '.join(mainMinusLine.split(' ')[:2])
|
||||
mainLines[1] = mainMinusLineStart + f' "{allCancelStr}"'
|
||||
mainLines.insert(3, f'alias -{self.primStr} "{mainMinusName}"')
|
||||
|
||||
return (
|
||||
'\n'.join(mainLines) + '\n',
|
||||
|
@ -548,7 +543,7 @@ class Double(Bind):
|
|||
)
|
||||
|
||||
|
||||
class Repeat(Bind):
|
||||
class Repeat(ScriptBind):
|
||||
def verify(self):
|
||||
self.interval = None
|
||||
self.command = None
|
||||
|
@ -578,7 +573,7 @@ class Repeat(Bind):
|
|||
return f'// repeat {self.key}\n'
|
||||
|
||||
|
||||
class Literal(Bind):
|
||||
class Literal(ScriptBind):
|
||||
def verify(self):
|
||||
self.text = ''
|
||||
self.run = False
|
||||
|
@ -622,8 +617,23 @@ class Literal(Bind):
|
|||
result += f'\n{self.key}'
|
||||
return result + '\n'
|
||||
|
||||
|
||||
class Bind(ScriptBind):
|
||||
def verify(self):
|
||||
# just set self.text, and if it's not a string, make an error
|
||||
self.text = self.fields
|
||||
if isinstance(self.text, str):
|
||||
# clear self.fields to assure no "extra field" warnings
|
||||
self.fields = {}
|
||||
else:
|
||||
# not passed string ==> error
|
||||
self.err('argument must be a string')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
return f'bind {self.key} {self.text}\n'
|
||||
|
||||
# This is at the bottom because it has to happen after
|
||||
# all inheritances have been completed
|
||||
|
||||
Bind.bindTypes = Bind.__subclasses__()
|
||||
Double.bindNames = [ bind.__name__.lower() for bind in Bind.bindTypes ]
|
||||
ScriptBind.bindTypes = ScriptBind.__subclasses__()
|
||||
Double.bindNames = [ bind.__name__.lower() for bind in ScriptBind.bindTypes ]
|
|
@ -47,7 +47,7 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
errMessages = []
|
||||
warnMessages = []
|
||||
for key, data in classCFG.items():
|
||||
bind = tftypes.Bind(key, data)
|
||||
bind = tftypes.ScriptBind(key, data)
|
||||
|
||||
bind = bind.toTargetType()
|
||||
if isclass:
|
Loading…
Reference in New Issue