Compare commits
4 Commits
a694c2a654
...
30cc02012e
Author | SHA1 | Date |
---|---|---|
Nicholas Hope | 30cc02012e | |
Nicholas Hope | 90f39b2737 | |
Nicholas Hope | 40c8b4ee42 | |
Nicholas Hope | 7248acb787 |
|
@ -2,26 +2,48 @@
|
|||
|
||||
# Used for the conditions in the <double> type
|
||||
condDict = {}
|
||||
defaultDict = {}
|
||||
bindOrAlias = "bind"
|
||||
from json import dumps
|
||||
from copy import deepcopy
|
||||
|
||||
def makeCFG(cfg, default=False):
|
||||
global bindOrAlias
|
||||
global condDict
|
||||
global defaultDict
|
||||
|
||||
bindOrAlias = "bind"
|
||||
if default:
|
||||
# Write to defaultDict instead of condDict
|
||||
condDict = defaultDict
|
||||
else:
|
||||
condDict = deepcopy(defaultDict)
|
||||
|
||||
def makeCFG(cfg):
|
||||
condDict.clear()
|
||||
ret = ''
|
||||
for key, data in cfg.items():
|
||||
# I know all of these fields exist because it was verified in verify.py
|
||||
bindType = firstTypeIn(data.keys())
|
||||
bindContent = data[bindType]
|
||||
ret += branch(key, bindContent, bindType)
|
||||
isAlias = False
|
||||
if "alias" in data:
|
||||
isAlias = data.pop("alias")
|
||||
if isAlias:
|
||||
bindOrAlias = "alias"
|
||||
else:
|
||||
bindOrAlias = "bind"
|
||||
ret += branch(key, data)
|
||||
|
||||
# Doubles are weird. All of the toggles got put into a dictionary.
|
||||
# This takes all of the nested dictionaries and turns them into the right string
|
||||
for key, toggles in condDict.items():
|
||||
ret += f'alias +{key}_toggles "{toggles["plus_toggles"]}"\n' +\
|
||||
f'alias -{key}_toggles "{toggles["minus_toggles"]}"\n' +\
|
||||
f'bind {key} "+{key}_toggles"\n'
|
||||
plusToggleStr = ';'.join(toggles["plus_toggles"])
|
||||
minusToggleStr = ';'.join(toggles["minus_toggles"])
|
||||
ret += f'alias +{key}_toggles "{plusToggleStr}"\n' +\
|
||||
f'alias -{key}_toggles "{minusToggleStr}"\n' +\
|
||||
f'{bindOrAlias} {key} "+{key}_toggles"\n'
|
||||
|
||||
del condDict # free deep copy
|
||||
|
||||
return ret
|
||||
|
||||
def firstTypeIn(inputList):
|
||||
def typeOf(dictIn):
|
||||
""" Find the first element common to both lists """
|
||||
types = [
|
||||
"impulse",
|
||||
|
@ -31,10 +53,13 @@ def firstTypeIn(inputList):
|
|||
"repeat"
|
||||
]
|
||||
for t in types:
|
||||
if t in inputList:
|
||||
if t in dictIn.keys():
|
||||
return t
|
||||
|
||||
def branch(keyName, bindContent, bindType):
|
||||
def branch(keyName, bindContent):
|
||||
bindType = typeOf(bindContent)
|
||||
bindContent = bindContent.pop(bindType)
|
||||
|
||||
if bindType == "impulse":
|
||||
return impulse(keyName, bindContent)
|
||||
|
||||
|
@ -54,6 +79,7 @@ def branch(keyName, bindContent, bindType):
|
|||
return repeat(keyName, bindContent)
|
||||
|
||||
def impulse(key, instruction):
|
||||
global bindOrAlias
|
||||
if isinstance(instruction, list):
|
||||
instruction = ';'.join(instruction)
|
||||
|
||||
|
@ -64,7 +90,7 @@ def impulse(key, instruction):
|
|||
|
||||
instruction = ';'.join(allInstructions)
|
||||
|
||||
return f'bind {key} "{instruction}"\n'
|
||||
return f'{bindOrAlias} {key} "{instruction}"\n'
|
||||
|
||||
def impulseShortcuts(instruction):
|
||||
splitCommand = instruction.split(' ')
|
||||
|
@ -116,13 +142,15 @@ def expandBuildings(building):
|
|||
return num
|
||||
|
||||
def simpleHold(key, instruction):
|
||||
global bindOrAlias
|
||||
# This isn't quite right, fix later!
|
||||
if instruction[0] != '+':
|
||||
return f'bind {key} "+{instruction}"\n'
|
||||
if instruction[0] == '+' or instruction[0] == '-':
|
||||
return f'{bindOrAlias} {key} "{instruction}"\n'
|
||||
else:
|
||||
return f'bind {key} "{instruction}"\n'
|
||||
return f'{bindOrAlias} {key} "+{instruction}"\n'
|
||||
|
||||
def listHold(key, options):
|
||||
global bindOrAlias
|
||||
pressStr = options["press"]
|
||||
if isinstance(pressStr, list):
|
||||
pressStr = ';'.join(pressStr)
|
||||
|
@ -133,10 +161,12 @@ def listHold(key, options):
|
|||
|
||||
ret = f'alias +{key}_bind "{pressStr}"\n' +\
|
||||
f'alias -{key}_bind "{releaseStr}"\n' +\
|
||||
f'bind {key} "+{key}_bind"\n'
|
||||
f'{bindOrAlias} {key} "+{key}_bind"\n'
|
||||
|
||||
return ret
|
||||
|
||||
def toggle(key, instruction):
|
||||
global bindOrAlias
|
||||
onStr = f'turn_{key}_on'
|
||||
offStr = f'turn_{key}_off'
|
||||
togStr = f'toggle_{key}'
|
||||
|
@ -144,59 +174,47 @@ def toggle(key, instruction):
|
|||
ret = f'alias {onStr} "+{instruction}; alias {togStr} {offStr}"\n' +\
|
||||
f'alias {offStr} "-{instruction}; alias {togStr} {onStr}"\n' +\
|
||||
f'alias {togStr} "{onStr}"\n' +\
|
||||
f'bind {key} "{togStr}"\n'
|
||||
f'{bindOrAlias} {key} "{togStr}"\n'
|
||||
return ret
|
||||
|
||||
def double(key, options):
|
||||
primaryAction = options["primary"]
|
||||
pBindType = firstTypeIn(primaryAction.keys())
|
||||
pBindContent = primaryAction[pBindType]
|
||||
|
||||
secAction = options["secondary"]
|
||||
sBindType = firstTypeIn(secAction.keys())
|
||||
sBindContent = secAction[sBindType]
|
||||
|
||||
mainStr = f'{key}_main'
|
||||
altStr = f'{key}_alt'
|
||||
togStr = f'toggle_{key}'
|
||||
pTogStr = f'+toggle_{key}'
|
||||
mTogStr = f'-toggle_{key}'
|
||||
|
||||
recursiveCode = branch(mainStr, pBindContent, pBindType) +\
|
||||
branch(altStr, sBindContent, sBindType)
|
||||
global bindOrAlias
|
||||
oldBindOrAlias = bindOrAlias
|
||||
bindOrAlias = "alias"
|
||||
recursiveCode = branch(mainStr, primaryAction) +\
|
||||
branch(altStr, secAction)
|
||||
bindOrAlias = oldBindOrAlias
|
||||
|
||||
newcode = []
|
||||
for line in recursiveCode.split('\n'):
|
||||
# For every line gotten by the recursive call, change all "bind"s to "alias",
|
||||
# since mainStr and altStr aren't valid bind targes
|
||||
llist = line.split(' ')
|
||||
for i in range(len(llist)):
|
||||
alphanumChars = ''.join(c for c in llist[i] if c.isalnum())
|
||||
if alphanumChars == 'bind':
|
||||
if llist[i][0].isalnum():
|
||||
llist[i] = 'alias'
|
||||
else:
|
||||
# If the first character isn't a normal character.
|
||||
# Almost always because it is a double quote
|
||||
llist[i] = llist[i][0] + 'alias'
|
||||
newcode.append(' '.join(llist))
|
||||
|
||||
ret = '\n'.join(newcode) +\
|
||||
f'alias +{togStr} "bind {key} {altStr}"\n' +\
|
||||
f'alias -{togStr} "bind {key} {mainStr}"\n'+\
|
||||
f'bind {key} "{mainStr}"\n'
|
||||
ret = recursiveCode +\
|
||||
f'alias {pTogStr} "{bindOrAlias} {key} {altStr}"\n' +\
|
||||
f'alias {mTogStr} "{bindOrAlias} {key} {mainStr}"\n'+\
|
||||
f'{bindOrAlias} {key} "{mainStr}"\n'
|
||||
|
||||
condName = options["condition"]
|
||||
|
||||
global condDict
|
||||
if condName in condDict:
|
||||
# If the condition key (like "mouse4") already has toggles,
|
||||
# just append another toggle string (it gets encased in quotes later)
|
||||
condDict[condName]["plus_toggles"] += f"; +{togStr}"
|
||||
condDict[condName]["minus_toggles"] += f"; -{togStr}"
|
||||
# just append another toggle string
|
||||
plusToggles = condDict[condName]["plus_toggles"]
|
||||
minusToggles = condDict[condName]["minus_toggles"]
|
||||
if pTogStr not in plusToggles:
|
||||
plusToggles.append(pTogStr)
|
||||
minusToggles.append(mTogStr)
|
||||
else:
|
||||
# If the condition key doesn't already exist, make it with the correct values
|
||||
# If the condition key doesn't already exist, make it
|
||||
condDict.update( {
|
||||
condName: {
|
||||
"plus_toggles": f"+{togStr}",
|
||||
"minus_toggles": f"-{togStr}"
|
||||
"plus_toggles": [ pTogStr ],
|
||||
"minus_toggles": [ mTogStr ]
|
||||
}
|
||||
} )
|
||||
|
||||
|
|
|
@ -40,8 +40,10 @@ def parseFile(inputFile) -> (dict, dict):
|
|||
# See verify.py
|
||||
config, aliases = verify.verifyConfig(config)
|
||||
if "errors" in config:
|
||||
for e in config["errors"]:
|
||||
print(e, file=stderr)
|
||||
for cclass, messages in config["errors"].items():
|
||||
print(f"Error in {cclass}:")
|
||||
for msg in messages:
|
||||
print(f" {msg}")
|
||||
return None, None
|
||||
else:
|
||||
return config, aliases
|
||||
|
@ -59,7 +61,7 @@ def parseConfig(config, defaults):
|
|||
tempsAndReals = {}
|
||||
|
||||
if defaults is not None:
|
||||
stringToWrite = tfscript.makeCFG(defaults)
|
||||
stringToWrite = tfscript.makeCFG(defaults, default=True)
|
||||
replaceDict = writing.writeOutput(stringToWrite, "default", args)
|
||||
tempsAndReals.update(replaceDict)
|
||||
|
||||
|
@ -129,10 +131,15 @@ def main() -> int:
|
|||
parser = parseCLI()
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
systemName = GetOSName()
|
||||
if args.directory is not None:
|
||||
targetDir = args.directory
|
||||
targetDir = normpath(args.directory)
|
||||
if systemName == "Windows":
|
||||
targetDir += '\\'
|
||||
else:
|
||||
targetDir += '/'
|
||||
else:
|
||||
systemName = GetOSName()
|
||||
targetDir = getTargetDir(systemName)
|
||||
if targetDir is not None:
|
||||
# Supported OS: add steamapps path
|
||||
|
|
|
@ -3,18 +3,22 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
verifiedConfig = {}
|
||||
|
||||
# Do defaults first
|
||||
aliasErrors = []
|
||||
errors = {}
|
||||
|
||||
defaults = None
|
||||
|
||||
if "default" in cfg:
|
||||
defaults = cfg.pop("default")
|
||||
errMessages = []
|
||||
for key, data in defaults.items():
|
||||
isAlias = ("alias" in data and data["alias"] == True)
|
||||
errMessages = validBind(key, data, alias = isAlias)
|
||||
if len(errMessages) > 0:
|
||||
for msg in errMessages:
|
||||
aliasErrors.append(f"Error in defaults: {msg}")
|
||||
isAlias = False
|
||||
if "alias" in data:
|
||||
isAlias = data["alias"]
|
||||
if not isinstance(isAlias, bool):
|
||||
errMessages.append(f'"alias" field in "{key}" makes no sense: "{isAlias}"')
|
||||
errMessages.extend(validBind(key, data, alias = isAlias) )
|
||||
if len(errMessages) > 0:
|
||||
errors.update( {"default": errMessages} )
|
||||
|
||||
classList = [
|
||||
"scout",
|
||||
|
@ -28,9 +32,6 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
"spy"
|
||||
]
|
||||
|
||||
errors = aliasErrors.copy()
|
||||
|
||||
|
||||
for cclass in classList:
|
||||
classCFG = None
|
||||
className = cclass
|
||||
|
@ -47,33 +48,35 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
# It may be less efficient this way, but
|
||||
# it makes for more descriptive error messages
|
||||
continue
|
||||
errMessages = []
|
||||
for key, data in classCFG.items():
|
||||
errMessages = []
|
||||
isAlias = False
|
||||
if "alias" in data:
|
||||
isAlias = data["alias"]
|
||||
if not isinstance(isAlias, bool):
|
||||
errMessages.append(f'Key "{key}" has alias not set to true or false. Did you accidentally put it in quotes?')
|
||||
errMessages.append(f'"alias" field in "{key}" makes no sense: "{isAlias}"')
|
||||
errMessages.extend( validBind(key, data, alias = isAlias) )
|
||||
if len(errMessages) > 0:
|
||||
for msg in errMessages:
|
||||
errors.append(f"Error in {cclass}: {msg}")
|
||||
if len(errMessages) > 0:
|
||||
errors.update( {className: errMessages} )
|
||||
verifiedConfig.update({className: classCFG})
|
||||
|
||||
# Turn list into only strings by expanding tuples
|
||||
for i, clss in enumerate(classList):
|
||||
if isinstance(clss, tuple):
|
||||
classList.insert(i+1, clss[0])
|
||||
classList.insert(i+1, clss[1])
|
||||
classList.insert(i+1, clss[0])
|
||||
classList.pop(i)
|
||||
|
||||
globalErrors = []
|
||||
for remainingClass in cfg:
|
||||
if remainingClass not in classList:
|
||||
errors.append(f'Error in {remainingClass}: "{remainingClass}" is not a valid class')
|
||||
globalErrors.append(f'"{remainingClass}" is not a valid class')
|
||||
else:
|
||||
otherName = findTwin(remainingClass)
|
||||
if otherName is not None:
|
||||
errors.append(f'Error in {remainingClass}: conflicting names for section: "{remainingClass}" and "{otherName}"')
|
||||
globalErrors.append(f'Conflicting names for section: "{remainingClass}" and "{otherName}"')
|
||||
|
||||
if len(globalErrors) > 0:
|
||||
errors.update( {"file": globalErrors} )
|
||||
|
||||
if len(errors) > 0:
|
||||
verifiedConfig.update({"errors": errors})
|
||||
|
@ -95,8 +98,8 @@ def validBind(key, data, alias = False) -> list:
|
|||
|
||||
extras = dataCopy.keys()
|
||||
if len(extras) > 0:
|
||||
extrasString = "\n\t".join(extras)
|
||||
ret.append(f'Unused fields in "{key}":\n\t{extrasString}')
|
||||
extrasString = "\n ".join(extras)
|
||||
ret.append(f'Unused fields in "{key}":\n {extrasString}')
|
||||
|
||||
return ret
|
||||
|
||||
|
@ -108,7 +111,10 @@ validKeyList = [
|
|||
'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'mouse1', 'mouse2', 'mouse3', 'mouse4', 'mouse5',
|
||||
'shift', 'capslock', 'ctrl', 'semicolon', 'space', 'enter',
|
||||
'backspace'
|
||||
'backspace',
|
||||
'scrolllock', 'numlock',
|
||||
'ins', 'home', 'pgup',
|
||||
'del', 'end', 'pgdn'
|
||||
]
|
||||
|
||||
def validKey(key):
|
||||
|
@ -144,6 +150,7 @@ def validBindType(key, data: dict):
|
|||
|
||||
def removeRelaventFields(data, bindType):
|
||||
errMsgs = []
|
||||
if "alias" in data: data.pop("alias")
|
||||
# These types are simple, just the bind type and argument
|
||||
if bindType in ["impulse", "toggle"]:
|
||||
data.pop(bindType)
|
||||
|
|
|
@ -55,7 +55,7 @@ def writeOutput(data, className, args) -> dict:
|
|||
|
||||
outfile.close() # the most-recent tempfile will not have been closed
|
||||
if args.debug:
|
||||
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', file=stderr)
|
||||
print( f'DEBUG: Wrote {bytesWritten} bytes to {className} ({fileNum}/{filesNeeded})', end='\n\n', file=stderr)
|
||||
|
||||
return namesDict
|
||||
|
||||
|
@ -70,6 +70,10 @@ def replaceFiles(targetDir, fileNames, args):
|
|||
if args.debug:
|
||||
print( f'DEBUG: Created {targetDir}{realName}', file=stderr)
|
||||
|
||||
if args.debug:
|
||||
# Break up the debug messages
|
||||
print(end='\n')
|
||||
|
||||
return list(fileNames.values())
|
||||
|
||||
def appendToActuals(targetDir, fileList, defaultsGiven, args):
|
||||
|
@ -87,6 +91,7 @@ def appendToActuals(targetDir, fileList, defaultsGiven, args):
|
|||
]
|
||||
for cclass in classList:
|
||||
addCallIfUncalled('exec default_script_1', targetDir, cclass, args)
|
||||
|
||||
fileList = onlyFirsts(fileList)
|
||||
for currFile in fileList:
|
||||
execStr = f'exec {currFile.split(".")[0]}'
|
||||
|
@ -98,10 +103,10 @@ def addCallIfUncalled(execStr, targetDir, fileName, args):
|
|||
realExists = exists(realFilePath)
|
||||
|
||||
# creates if it doesn't exist, so must come after the exists() call
|
||||
cfgFile = open(realFilePath, 'r+')
|
||||
cfgFile = open(realFilePath, 'a+')
|
||||
if not realExists:
|
||||
if args.debug:
|
||||
print( f"DEBUG: Created file {targetDir}{realFilePath}" )
|
||||
print( f"DEBUG: Created {realFilePath}" )
|
||||
cfgFile.write(execStr + '\n')
|
||||
|
||||
elif not strInFile(execStr, cfgFile):
|
||||
|
@ -132,10 +137,12 @@ def getRealName(fileName):
|
|||
return className + '.cfg'
|
||||
|
||||
def strInFile(execStr, f):
|
||||
lineList = [ ' '.join(line.split()) for line in f.readlines() ]
|
||||
# Opened in append mode, so cursor is at the end.
|
||||
# Must reopen to put cursor at the start.
|
||||
with open(f.name, 'r') as dupfile:
|
||||
lineList = [ ' '.join(line.split()) for line in dupfile.readlines() ]
|
||||
for line in lineList:
|
||||
# Remove indent and outdent, including trailing newline
|
||||
print(line)
|
||||
if execStr == line:
|
||||
return True
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
default:
|
||||
e:
|
||||
double:
|
||||
primary:
|
||||
|
||||
double:
|
||||
primary:
|
||||
impulse: voice medic
|
||||
secondary:
|
||||
impulse: voice activate uber
|
||||
condition: mouse5
|
||||
|
||||
secondary:
|
||||
impulse: voice uber ready
|
||||
condition: mouse4
|
Loading…
Reference in New Issue