forked from SilverStudioGames/WTS
Gouvernathor
36582d0f9c
some uses of str.format remain, but converting them would be more trouble than it's worth (cherry picked from commit f17cffa3ec5329988a58c76f8fa4f3fe4846a6fc)
270 lines
11 KiB
Plaintext
270 lines
11 KiB
Plaintext
init:
|
|
if config.developer:
|
|
# Dummy code used to stop lint from reporting false errors.
|
|
image snape_main = Null()
|
|
image genie_main = Null()
|
|
image tonks_main = Null()
|
|
image hermione_main = Null()
|
|
image cho_main = Null()
|
|
image luna_main = Null()
|
|
image astoria_main = Null()
|
|
image susan_main = Null()
|
|
image hooch_main = Null()
|
|
image hooch = Null()
|
|
|
|
init -1 python:
|
|
if config.developer:
|
|
|
|
def lint_characters():
|
|
renpy.execute_default_statement(False)
|
|
|
|
# Add images to linting list to avoid undefined errors
|
|
for i in states.dolls:
|
|
prefix = get_character_tag(i)
|
|
renpy.lint.image_prefixes[prefix] = True
|
|
|
|
nodes = [i for i in renpy.game.script.all_stmts if isinstance(i, (renpy.ast.Say, renpy.ast.Menu))]
|
|
nodes.sort(key=lambda x: (x.filename, x.linenumber))
|
|
files = renpy.list_files()
|
|
|
|
SELF_CLOSING_TAGS = re.compile(r'(\{\{)|(\{(p|w|nw|fast|done|image|space)(?:\=([^}]*))?\})', re.S)
|
|
|
|
EXPRESSIONS = ["mouth", "eyes", "eyebrows", "pupils", "cheeks", "tears"]
|
|
POSITIONS = ["xpos", "ypos", "pos"]
|
|
OTHER = ["flip", "trans", "animation", "hair", "emote"]
|
|
KEYS = EXPRESSIONS + POSITIONS + OTHER
|
|
INTERPUNCT = (".", "!", "?", "--", "*", ")", "}")
|
|
SAYERS = {i[:3]:i for i in states.dolls}
|
|
RE_SPACES = re.compile(r"\s[^\S\r\n]")
|
|
RE_COMMA = re.compile(r"\w+, \w+, \w+ and")
|
|
RE_BRACKET = re.compile(r"\([^)]*$")
|
|
RE_REPEATED = re.compile(r"\b(\w+)\b \b\1\b")
|
|
RE_QUOTES = re.compile(r"[.,]\\\"")
|
|
RE_ARTICLES = re.compile(r" a [aeiou][^uni|eu|ur|usa]")
|
|
|
|
for node in nodes:
|
|
|
|
if isinstance(node, renpy.ast.Say):
|
|
who = node.who
|
|
what = [node.what]
|
|
file = node.filename
|
|
line = node.linenumber
|
|
args = node.arguments
|
|
code = node.get_code()
|
|
type = "say"
|
|
elif isinstance(node, renpy.ast.Menu):
|
|
who = None
|
|
what = [x[0] for x in node.items]
|
|
file = node.filename
|
|
line = node.linenumber
|
|
args = None
|
|
code = None
|
|
type = "menu"
|
|
|
|
has_failed = False
|
|
has_xpos = False
|
|
has_ypos = False
|
|
uses_ypos_head = False
|
|
|
|
if args:
|
|
kwargs = dict.fromkeys(KEYS)
|
|
|
|
args = args.arguments
|
|
|
|
for i, arg in enumerate(args):
|
|
key, val = arg
|
|
|
|
if key is None:
|
|
kwargs[KEYS[i]] = val
|
|
else:
|
|
kwargs[key] = val
|
|
|
|
for key, val in list(kwargs.items()):
|
|
# Validate facial expressions
|
|
if key in EXPRESSIONS and who in iter(list(SAYERS.keys())):
|
|
|
|
if val is None:
|
|
continue
|
|
|
|
if not val.startswith(("\"", "'")) and not val.endswith(("\"", "'")): # Ignore variables
|
|
continue
|
|
|
|
# Node argument values are (fucking) raw
|
|
val = strip(val)
|
|
|
|
msg = repr(key)
|
|
fp = f"characters/{SAYERS.get(who)}/poses/default/face/{key}/{val}/"
|
|
fn = next((f for f in files if f.startswith(fp)), f"{fp}expression.webp")
|
|
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
renpy.lint.check_file(msg, fn)
|
|
|
|
# Validate positional arguments
|
|
if key in POSITIONS:
|
|
if val is None:
|
|
continue
|
|
|
|
# Node argument values are (fucking) raw
|
|
val = strip(val)
|
|
|
|
# Skip integers
|
|
try:
|
|
int(val)
|
|
continue
|
|
except:
|
|
pass
|
|
|
|
if key == "xpos":
|
|
has_xpos = True
|
|
|
|
val = sprite_pos.get("x").get(val, val)
|
|
|
|
elif key == "ypos":
|
|
has_ypos = True
|
|
|
|
if val == "head":
|
|
uses_ypos_head = True
|
|
|
|
val = sprite_pos.get("y").get(val, val)
|
|
|
|
if not isinstance(val, int):
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = f"{key!r} requires an integer, or a pre-defined named position, not {val!r}"
|
|
renpy.lint.report(msg)
|
|
|
|
# This would require fixing hundreds of calls. Might postpone it...
|
|
|
|
# if uses_ypos_head and not has_xpos:
|
|
# if not has_failed:
|
|
# # Avoid repeating node destination
|
|
# renpy.lint.report_node = node
|
|
# has_failed = True
|
|
|
|
# msg = "'ypos' uses pre-defined 'head' position, but is lacking 'xpos'"
|
|
# renpy.lint.report(msg)
|
|
|
|
if key in OTHER:
|
|
|
|
if val is None:
|
|
continue
|
|
|
|
# Node argument values are (fucking) raw
|
|
val = strip(val)
|
|
|
|
if key == "emote":
|
|
msg = repr(key)
|
|
fn = f"characters/{SAYERS.get(who)}/emote/{val}.webp"
|
|
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
renpy.lint.check_file(msg, fn)
|
|
|
|
for string in what:
|
|
|
|
if not string:
|
|
continue
|
|
|
|
if string.startswith("[") and string.endswith("]"): # Ignore string interpolation
|
|
continue
|
|
|
|
if "{#LINT_IGNORE}" in string:
|
|
continue
|
|
|
|
# Check for multiple spaces inside 'what' string
|
|
match = re.search(RE_SPACES, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string contains multiple spaces"
|
|
renpy.lint.report(msg)
|
|
|
|
# Check for the (lack of) oxford comma inside 'what' string
|
|
match = re.search(RE_COMMA, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string is missing oxford comma(s)"
|
|
renpy.lint.report(msg)
|
|
|
|
# Check for missing brackets inside 'what' string
|
|
match = re.search(RE_BRACKET, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string is missing a closing bracket"
|
|
renpy.lint.report(msg)
|
|
|
|
# Check for repeated words words inside 'what' string
|
|
match = re.search(RE_REPEATED, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string contains repeated words"
|
|
renpy.lint.report(msg)
|
|
|
|
# Check for punctuation inside quotes, inside 'what' string
|
|
match = re.search(RE_QUOTES, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string contains non-british punctuation for quotes"
|
|
renpy.lint.report(msg)
|
|
|
|
# Check for wrong articles inside 'what' string
|
|
match = re.search(RE_ARTICLES, string.strip())
|
|
|
|
if match:
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string contains wrong articles"
|
|
renpy.lint.report(msg)
|
|
|
|
if type == "menu" and string.startswith(("-", "\"")) and string.endswith(("-", "\"")):
|
|
continue
|
|
|
|
# Check punctuation errors
|
|
if not string.endswith(INTERPUNCT):
|
|
if not has_failed:
|
|
# Avoid repeating node destination
|
|
renpy.lint.report_node = node
|
|
has_failed = True
|
|
|
|
msg = type + " string punctuation is not adhering to the set text styling"
|
|
renpy.lint.report(msg)
|
|
|
|
config.lint_hooks.append(lint_characters)
|