diff --git a/.gitignore b/.gitignore
index 05ea37876..5d4d35fa8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ nml/__version__.py
nml.egg-info/*
nml_lz77*
regression/*output*/*
+.python-version
diff --git a/nml/actions/action0.py b/nml/actions/action0.py
index be0249100..0c8e2bde9 100644
--- a/nml/actions/action0.py
+++ b/nml/actions/action0.py
@@ -221,7 +221,7 @@ def print_stats():
for feature in used_ids:
used = feature.get_num_allocated()
if used > 0 and feature.dynamic_allocation:
- generic.print_info("{} items: {}/{}".format(feature.name, used, feature.get_max_allocated()))
+ generic.print_info(f"{feature.name} items: {used}/{feature.get_max_allocated()}")
def mark_id_used(feature, id, num_ids):
@@ -263,8 +263,7 @@ def check_id_range(feature, id, num_ids, pos):
# Check that IDs are valid and in range.
if not blk_alloc.in_range(id, num_ids):
- msg = "Item ID must be in range 0..{:d}, encountered {:d}..{:d}."
- msg = msg.format(blk_alloc.last, id, id + num_ids - 1)
+ msg = f"Item ID must be in range 0..{blk_alloc.last}, encountered {id}..{id + num_ids - 1}."
raise generic.ScriptError(msg, pos)
# ID already defined, but with the same size: OK
@@ -279,18 +278,15 @@ def check_id_range(feature, id, num_ids, pos):
if blk_alloc.get_size(id) is not None:
# ID already defined with a different size: error.
- raise generic.ScriptError(
- "Item with ID {:d} has already been defined, but with a different size.".format(id), pos
- )
+ raise generic.ScriptError(f"Item with ID {id} has already been defined, but with a different size.", pos)
if blk_alloc.is_address_free(id):
# First item id free -> any of the additional tile ids must be blocked.
- msg = "This multi-tile house requires that item IDs {:d}..{:d} are free, but they are not."
- msg = msg.format(id, id + num_ids - 1)
+ msg = f"This multi-tile house requires that item IDs {id}..{id + num_ids - 1} are free, but they are not."
raise generic.ScriptError(msg, pos)
# ID already defined as part of a multi-tile house.
- raise generic.ScriptError("Item ID {:d} has already used as part of a multi-tile house.".format(id), pos)
+ raise generic.ScriptError(f"Item ID {id} has already used as part of a multi-tile house.", pos)
def get_free_id(feature, num_ids, pos):
@@ -310,8 +306,7 @@ def get_free_id(feature, num_ids, pos):
addr = blk_alloc.find_unused(num_ids)
if addr is None:
- msg = "Unable to allocate ID for item, no more free IDs available (maximum is {:d})"
- msg = msg.format(blk_alloc.last)
+ msg = f"Unable to allocate ID for item, no more free IDs available (maximum is {blk_alloc.last})"
raise generic.ScriptError(msg, pos)
blk_alloc.mark_used(addr, num_ids)
@@ -449,9 +444,8 @@ def get_property_info_list(feature, name):
assert feature in range(0, len(properties)) # guaranteed by item
if properties[feature] is None:
raise generic.ScriptError(
- "Setting properties for feature '{}' is not possible, no properties are defined.".format(
- general.feature_name(feature)
- ),
+ f"Setting properties for feature '{general.feature_name(feature)}' is not possible,"
+ " no properties are defined.",
name.pos,
)
@@ -615,13 +609,11 @@ def apply_threshold(value):
# This can be used to set a label (=string of length 4) to the value of a parameter.
if not isinstance(value, expression.StringLiteral):
raise generic.ScriptError(
- "Value for property {:d} must be a string literal".format(prop_info["num"]), value.pos
+ f"Value for property {prop_info['num']} must be a string literal", value.pos
)
if grfstrings.get_string_size(value.value, False, True) != prop_info["string_literal"]:
raise generic.ScriptError(
- "Value for property {:d} must be of length {:d}".format(
- prop_info["num"], prop_info["string_literal"]
- ),
+ f"Value for property {prop_info['num']} must be of length {prop_info['string_literal']}",
value.pos,
)
@@ -635,7 +627,7 @@ def apply_threshold(value):
elif isinstance(value, expression.String):
if "string" not in prop_info:
raise generic.ScriptError(
- "String used as value for non-string property: " + str(prop_info["num"]), value.pos
+ f"String used as value for non-string property: {prop_info['num']}", value.pos
)
string_range = apply_threshold(prop_info["string"])
stringid, string_actions = action4.get_string_action4s(feature, string_range, value, id)
@@ -683,14 +675,14 @@ def validate_prop_info_list(prop_info_list, pos_list, feature):
if info in prop_info:
generic.print_warning(
generic.Warning.GENERIC,
- "Property '{}' should be set before all other properties and graphics.".format(prop_name),
+ f"Property '{prop_name}' should be set before all other properties and graphics.",
pos,
)
break
for info in prop_info:
if "required" in info and info not in prop_info_list:
generic.print_error(
- "Property '{}' is not set. Item will be invalid.".format(prop_name),
+ f"Property '{prop_name}' is not set. Item will be invalid.",
pos_list[-1],
)
@@ -1027,7 +1019,7 @@ def get_disable_actions(disable):
"""
feature = disable.feature.value
if feature not in disable_info:
- raise generic.ScriptError("disable_item() is not available for feature {:d}.".format(feature), disable.pos)
+ raise generic.ScriptError(f"disable_item() is not available for feature {feature}.", disable.pos)
if disable.first_id is None:
# No ids set -> disable all
assert disable.last_id is None
diff --git a/nml/actions/action0properties.py b/nml/actions/action0properties.py
index 8e91963b0..79bbe9c1a 100644
--- a/nml/actions/action0properties.py
+++ b/nml/actions/action0properties.py
@@ -15,7 +15,7 @@
import itertools
-from nml import generic, nmlop, global_constants
+from nml import generic, global_constants, nmlop
from nml.expression import (
AcceptCargo,
Array,
@@ -43,7 +43,7 @@ def write(self, file):
@param file: The outputfile we have to write to.
@type file: L{SpriteOutputBase}
"""
- raise NotImplementedError("write is not implemented in {!r}".format(type(self)))
+ raise NotImplementedError(f"write is not implemented in {type(self)!r}")
def get_size(self):
"""
@@ -53,7 +53,7 @@ def get_size(self):
@return: The size of this property in bytes.
@rtype: C{int}
"""
- raise NotImplementedError("get_size is not implemented in {!r}".format(type(self)))
+ raise NotImplementedError(f"get_size is not implemented in {type(self)!r}")
class Action0Property(BaseAction0Property):
@@ -261,9 +261,7 @@ def cargo_list(value, max_num_cargos):
@type prop_size: C{int}
"""
if not isinstance(value, Array) or len(value.values) > max_num_cargos:
- raise generic.ScriptError(
- "Cargo list must be an array with no more than {:d} values".format(max_num_cargos), value.pos
- )
+ raise generic.ScriptError(f"Cargo list must be an array with no more than {max_num_cargos} values", value.pos)
cargoes = value.values + [ConstantNumeric(0xFF, value.pos) for _ in range(max_num_cargos - len(value.values))]
ret = None
@@ -419,7 +417,7 @@ def single_or_list(prop_name, single_num, multiple_num, value):
if len(value.values) == 1:
return [Action0Property(single_num, value.values[0].reduce_constant(), 1)]
return [VariableByteListProp(multiple_num, [[type.reduce_constant().value for type in value.values]])]
- raise generic.ScriptError("'{}' must be a constant or an array of constants".format(prop_name))
+ raise generic.ScriptError(f"'{prop_name}' must be a constant or an array of constants")
#
@@ -785,10 +783,8 @@ def station_layouts(value):
length = len(layout.values[0].values)
number = len(layout.values)
if (length, number) in layouts:
- generic.print_warning(
- generic.Warning.GENERIC, "Redefinition of layout {}x{}".format(length, number), layout.pos
- )
- layouts[(length, number)] = []
+ generic.print_warning(generic.Warning.GENERIC, f"Redefinition of layout {length}x{number}", layout.pos)
+ layouts[length, number] = []
for platform in layout.values:
if not isinstance(platform, Array) or len(platform.values) == 0:
raise generic.ScriptError("A platform must be an array of tile types")
@@ -798,10 +794,10 @@ def station_layouts(value):
if type.reduce_constant().value % 2 != 0:
generic.print_warning(
generic.Warning.GENERIC,
- "Invalid tile {} in layout {}x{}".format(type, length, number),
+ f"Invalid tile {type} in layout {length}x{number}",
type.pos,
)
- layouts[(length, number)].append(
+ layouts[length, number].append(
[nmlop.AND(type, 0xFE).reduce_constant().value for type in platform.values]
)
return [StationLayoutProp(0x0E, layouts)]
@@ -1127,7 +1123,7 @@ def industry_layouts(value):
layouts = []
for name in value.values:
if name.value not in tilelayout_names:
- raise generic.ScriptError("Unknown layout name '{}'".format(name.value), name.pos)
+ raise generic.ScriptError(f"Unknown layout name '{name.value}'", name.pos)
layouts.append(tilelayout_names[name.value])
return [IndustryLayoutProp(layouts)]
@@ -1443,7 +1439,7 @@ def airport_layouts(value):
layouts = []
for name in value.values:
if name.value not in tilelayout_names:
- raise generic.ScriptError("Unknown layout name '{}'".format(name.value), name.pos)
+ raise generic.ScriptError(f"Unknown layout name '{name.value}'", name.pos)
layout = tilelayout_names[name.value]
if "rotation" not in layout.properties:
raise generic.ScriptError("Airport layouts must have the 'rotation' property", layout.pos)
diff --git a/nml/actions/action1.py b/nml/actions/action1.py
index fcdf472eb..b439602c7 100644
--- a/nml/actions/action1.py
+++ b/nml/actions/action1.py
@@ -13,7 +13,7 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-from nml.actions import base_action, real_sprite, action2
+from nml.actions import action2, base_action, real_sprite
class Action1(base_action.BaseAction):
@@ -231,10 +231,13 @@ def get_action1_index(spriteset, feature):
@rtype: C{int}
"""
assert feature in spriteset_collections
+ act1_idx = None
for spriteset_collection in spriteset_collections[feature]:
if spriteset in spriteset_collection.spritesets:
- return spriteset_collection.get_index(spriteset)
- assert False
+ act1_idx = spriteset_collection.get_index(spriteset)
+ break
+ assert act1_idx is not None
+ return act1_idx
def make_cb_failure_action1(feature):
diff --git a/nml/actions/action11.py b/nml/actions/action11.py
index 6cf698d79..e977fb092 100644
--- a/nml/actions/action11.py
+++ b/nml/actions/action11.py
@@ -1,3 +1,7 @@
+"""
+Action 11 support classes (sounds).
+"""
+
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,9 +17,6 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-"""
-Action 11 support classes (sounds).
-"""
import os
from nml import generic
@@ -45,12 +46,10 @@ def __init__(self, fname, pos):
def prepare_output(self, sprite_num):
if not os.access(self.fname, os.R_OK):
- raise generic.ScriptError("Sound file '{}' does not exist.".format(self.fname), self.pos)
+ raise generic.ScriptError(f"Sound file '{self.fname}' does not exist.", self.pos)
size = os.path.getsize(self.fname)
if size == 0:
- raise generic.ScriptError(
- "Expected sound file '{}' to have a non-zero length.".format(self.fname), self.pos
- )
+ raise generic.ScriptError(f"Expected sound file '{self.fname}' to have a non-zero length.", self.pos)
def write(self, file):
file.print_named_filedata(self.fname)
@@ -102,7 +101,7 @@ def print_stats():
"""
if len(registered_sounds) > 0:
# Currently NML does not optimise the order of sound effects. So we assume NUM_ANIMATION_SOUNDS as the maximum.
- generic.print_info("Sound effects: {}/{}".format(len(registered_sounds), NUM_ANIMATION_SOUNDS))
+ generic.print_info(f"Sound effects: {len(registered_sounds)}/{NUM_ANIMATION_SOUNDS}")
def add_sound(args, pos):
diff --git a/nml/actions/action14.py b/nml/actions/action14.py
index 1d5a45a08..3cc7ab5fe 100644
--- a/nml/actions/action14.py
+++ b/nml/actions/action14.py
@@ -87,7 +87,7 @@ def get_size(self):
@return: The size (in bytes) of this node.
"""
- raise NotImplementedError("get_size must be implemented in Action14Node-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"get_size must be implemented in Action14Node-subclass {type(self)!r}")
def write(self, file):
"""
@@ -95,7 +95,7 @@ def write(self, file):
@param file: The file to write the output to.
"""
- raise NotImplementedError("write must be implemented in Action14Node-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"write must be implemented in Action14Node-subclass {type(self)!r}")
def write_type_id(self, file):
file.print_string(self.type_string, False, True)
@@ -266,7 +266,7 @@ def param_desc_actions(root, params):
if min_val > max_val or def_val < min_val or def_val > max_val:
generic.print_warning(
generic.Warning.GENERIC,
- "Limits for GRF parameter {} are incoherent, ignoring.".format(param_num),
+ f"Limits for GRF parameter {param_num} are incoherent, ignoring.",
)
min_val = 0
max_val = 0xFFFFFFFF
diff --git a/nml/actions/action2.py b/nml/actions/action2.py
index 0ccdf2950..92b869b3f 100644
--- a/nml/actions/action2.py
+++ b/nml/actions/action2.py
@@ -44,15 +44,11 @@ def print_stats():
"""
if spritegroup_stats[0] > 0:
generic.print_info(
- "Concurrent spritegroups: {}/{} ({})".format(
- spritegroup_stats[0], total_action2_ids, str(spritegroup_stats[1])
- )
+ f"Concurrent spritegroups: {spritegroup_stats[0]}/{total_action2_ids} ({spritegroup_stats[1]})"
)
if a2register_stats[0] > 0:
generic.print_info(
- "Concurrent Action2 registers: {}/{} ({})".format(
- a2register_stats[0], total_tmp_locations, str(a2register_stats[1])
- )
+ f"Concurrent Action2 registers: {a2register_stats[0]}/{total_tmp_locations} ({a2register_stats[1]})"
)
@@ -117,7 +113,7 @@ def prepare_output(self, sprite_num):
)
def write_sprite_start(self, file, size, extra_comment=None):
- assert self.num_refs == 0, "Action2 reference counting has {:d} dangling references.".format(self.num_refs)
+ assert self.num_refs == 0, f"Action2 reference counting has {self.num_refs} dangling references."
file.comment("Name: " + self.name)
if extra_comment:
for c in extra_comment:
@@ -298,10 +294,8 @@ def __init__(self):
Instead, call initialize(...).
"""
raise NotImplementedError(
- (
- "__init__ must be implemented in ASTSpriteGroup-subclass {!r},"
- " initialize(..) should be called instead"
- ).format(type(self))
+ f"__init__ must be implemented in ASTSpriteGroup-subclass {type(self)!r},"
+ " initialize(..) should be called instead"
)
def initialize(self, name=None, feature=None, num_params=0):
@@ -389,7 +383,7 @@ def prepare_act2_output(self):
assert self.name is not None
generic.print_warning(
generic.Warning.OPTIMISATION,
- "Block '{}' is not referenced, ignoring.".format(self.name.value),
+ f"Block '{self.name.value}' is not referenced, ignoring.",
self.pos,
)
@@ -433,7 +427,7 @@ def collect_references(self):
@rtype: C{iterable} of L{SpriteGroupRef}
"""
raise NotImplementedError(
- "collect_references must be implemented in ASTSpriteGroup-subclass {!r}".format(type(self))
+ f"collect_references must be implemented in ASTSpriteGroup-subclass {type(self)!r}"
)
def set_action2(self, action2, feature):
@@ -492,16 +486,14 @@ def _add_reference(self, target_ref):
# Passing parameters is not possible here
if len(target_ref.param_list) != 0:
raise generic.ScriptError(
- "Passing parameters to '{}' is only possible from a spritelayout.".format(
- target_ref.name.value
- ),
+ f"Passing parameters to '{target_ref.name.value}' is only possible from a spritelayout.",
target_ref.pos,
)
self.used_sprite_sets.append(target)
else:
if len(target_ref.param_list) != target.num_params:
- msg = "'{}' expects {:d} parameters, encountered {:d}."
+ msg = "'{}' expects {} parameters, encountered {}."
msg = msg.format(target_ref.name.value, target.num_params, len(target_ref.param_list))
raise generic.ScriptError(msg, target_ref.pos)
@@ -543,7 +535,7 @@ def register_spritegroup(spritegroup):
"""
name = spritegroup.name.value
if name in spritegroup_list:
- raise generic.ScriptError("Block with name '{}' has already been defined".format(name), spritegroup.pos)
+ raise generic.ScriptError(f"Block with name '{name}' has already been defined", spritegroup.pos)
spritegroup_list[name] = spritegroup
global_constants.spritegroups[name] = name
@@ -559,5 +551,5 @@ def resolve_spritegroup(name):
@rtype: L{ASTSpriteGroup}
"""
if name.value not in spritegroup_list:
- raise generic.ScriptError("Unknown identifier encountered: '{}'".format(name.value), name.pos)
+ raise generic.ScriptError(f"Unknown identifier encountered: '{name.value}'", name.pos)
return spritegroup_list[name.value]
diff --git a/nml/actions/action2layout.py b/nml/actions/action2layout.py
index 1371e6340..e632d1000 100644
--- a/nml/actions/action2layout.py
+++ b/nml/actions/action2layout.py
@@ -39,7 +39,7 @@ def resolve_tmp_storage(self):
def write(self, file):
size = self.layout.get_size()
- regs = ["{} : register {:X}".format(reg.name, reg.register) for reg in self.param_registers]
+ regs = [f"{reg.name} : register {reg.register:X}" for reg in self.param_registers]
action2.Action2.write_sprite_start(self, file, size, regs)
self.layout.write(file)
file.end_sprite()
@@ -231,9 +231,9 @@ def set_param(self, name, value):
name = name.value
if name not in self.params:
- raise generic.ScriptError("Unknown sprite parameter '{}'".format(name), value.pos)
+ raise generic.ScriptError(f"Unknown sprite parameter '{name}'", value.pos)
if self.is_set(name):
- raise generic.ScriptError("Sprite parameter '{}' can be set only once per sprite.".format(name), value.pos)
+ raise generic.ScriptError(f"Sprite parameter '{name}' can be set only once per sprite.", value.pos)
self.params[name]["value"] = self.params[name]["validator"](name, value)
self.params[name]["is_set"] = True
@@ -371,9 +371,7 @@ def _validate_bounding_box(self, name, value):
else:
assert name in ("xextent", "yextent", "zextent")
if not isinstance(value, expression.ConstantNumeric):
- raise generic.ScriptError(
- "Value of '{}' must be a compile-time constant number.".format(name), value.pos
- )
+ raise generic.ScriptError(f"Value of '{name}' must be a compile-time constant number.", value.pos)
generic.check_range(value.value, 0, 255, name, value.pos)
return value
# Value must be written to a register
@@ -472,9 +470,7 @@ def process(self, spritelayout, feature, param_map, actions, layout_registers, v
for type, pos, param_list in layout_sprite_list:
if type.value not in layout_sprite_types:
raise generic.ScriptError(
- "Invalid sprite type '{}' encountered. Expected 'ground', 'building', or 'childsprite'.".format(
- type.value
- ),
+ f"Invalid sprite type '{type.value}' encountered. Expected 'ground', 'building', or 'childsprite'.",
type.pos,
)
sprite = Action2LayoutSprite(feature, layout_sprite_types[type.value], layout_registers, pos, param_map)
@@ -535,9 +531,7 @@ def get_layout_action2s(spritelayout, feature):
actions = []
if feature not in action2.features_sprite_layout:
- raise generic.ScriptError(
- "Sprite layouts are not supported for feature '{}'.".format(general.feature_name(feature))
- )
+ raise generic.ScriptError(f"Sprite layouts are not supported for feature '{general.feature_name(feature)}'.")
# Allocate registers
param_map = {}
@@ -563,7 +557,7 @@ def get_layout_action2s(spritelayout, feature):
layout_action = Action2Layout(
feature,
- spritelayout.name.value + " - feature {:02X}".format(feature),
+ spritelayout.name.value + f" - feature {feature:02X}",
spritelayout.pos,
layout,
param_registers,
@@ -587,7 +581,7 @@ def get_layout_action2s(spritelayout, feature):
actions.append(extra_act6)
varaction2 = action2var.Action2Var(
- feature, "{}@registers - feature {:02X}".format(spritelayout.name.value, feature), spritelayout.pos, 0x89
+ feature, f"{spritelayout.name.value}@registers - feature {feature:02X}", spritelayout.pos, 0x89
)
varaction2.var_list = varact2parser.var_list
ref = expression.SpriteGroupRef(spritelayout.name, [], None, layout_action)
@@ -627,7 +621,7 @@ def make_empty_layout_action2(feature, pos):
layout = ParsedSpriteLayout()
layout.ground_sprite = Action2LayoutSprite(feature, Action2LayoutSpriteType.GROUND)
layout.ground_sprite.set_param(expression.Identifier("sprite"), expression.ConstantNumeric(0))
- return Action2Layout(feature, "@CB_FAILED_LAYOUT{:02X}".format(feature), pos, layout, [])
+ return Action2Layout(feature, f"@CB_FAILED_LAYOUT{feature:02X}", pos, layout, [])
class StationSpriteset(expression.Expression):
@@ -660,7 +654,7 @@ def append_mapping(self, mapping, feature, actions, default, custom_spritesets):
actions.extend(action1.add_to_action1([spriteset], feature, None))
real_action2 = action2real.make_simple_real_action2(
feature,
- spriteset.name.value + " - feature {:02X}".format(feature),
+ spriteset.name.value + f" - feature {feature:02X}",
None,
action1.get_action1_index(spriteset, feature),
)
@@ -686,9 +680,9 @@ def parse_station_layouts(feature, id, layouts):
def parse_spriteset(name, args, pos, info):
if info:
if len(args) < 1:
- raise generic.ScriptError("'{}' expects 1 or 2 parameters".format(name), pos)
+ raise generic.ScriptError(f"'{name}' expects 1 or 2 parameters", pos)
if not isinstance(args[0], expression.ConstantNumeric):
- raise generic.ScriptError("First parameter for '{}' must be a constant".format(name), pos)
+ raise generic.ScriptError(f"First parameter for '{name}' must be a constant", pos)
return var10map.translate(args[0].value, args[1:], pos)
return StationSpriteset(None, args, info, pos)
@@ -751,7 +745,7 @@ def parse_spriteset(name, args, pos, info):
actions.append(extra_act6)
varaction2 = action2var.Action2Var(
- feature, "Station Layout@registers - Id {:02X}".format(id.value), None, 0x89, param_registers
+ feature, f"Station Layout@registers - Id {id.value:02X}", None, 0x89, param_registers
)
varaction2.var_list = varact2parser.var_list
varaction2.default_result = expression.ConstantNumeric(0)
diff --git a/nml/actions/action2production.py b/nml/actions/action2production.py
index 07f7388a7..2ddaebf8d 100644
--- a/nml/actions/action2production.py
+++ b/nml/actions/action2production.py
@@ -111,7 +111,7 @@ def finish_production_actions(produce, prod_action, action_list, varact2parser):
produce.set_action2(prod_action, 0x0A)
else:
# Create intermediate varaction2
- varaction2 = action2var.Action2Var(0x0A, "{}@registers".format(produce.name.value), produce.pos, 0x89)
+ varaction2 = action2var.Action2Var(0x0A, f"{produce.name.value}@registers", produce.pos, 0x89)
varaction2.var_list = varact2parser.var_list
action_list.extend(varact2parser.extra_actions)
extra_act6 = action6.Action6()
@@ -185,7 +185,7 @@ def get_production_v2_actions(produce):
def resolve_cargoitem(item):
cargolabel = item.name.value
if cargolabel not in global_constants.cargo_numbers:
- raise generic.ScriptError("Cargo label {0} not found in your cargo table".format(cargolabel), produce.pos)
+ raise generic.ScriptError(f"Cargo label {cargolabel} not found in your cargo table", produce.pos)
cargoindex = global_constants.cargo_numbers[cargolabel]
valueregister = resolve_prodcb_register(item.value, varact2parser)
return (cargoindex, valueregister)
diff --git a/nml/actions/action2random.py b/nml/actions/action2random.py
index 04343778f..ff855dc89 100644
--- a/nml/actions/action2random.py
+++ b/nml/actions/action2random.py
@@ -126,7 +126,7 @@ def parse_randomswitch_type(random_switch):
# Validate type name / param combination
if type_str not in random_types[feature_val]:
raise generic.ScriptError(
- "Invalid combination for random_switch feature {:d} and type '{}'. ".format(feature_val, type_str), type_pos
+ f"Invalid combination for random_switch feature {feature_val} and type '{type_str}'. ", type_pos
)
type_info = random_types[feature_val][type_str]
@@ -135,14 +135,14 @@ def parse_randomswitch_type(random_switch):
# No param given
if type_info["param"] == 1:
raise generic.ScriptError(
- "Value '{}' for random_switch parameter 2 'type' requires a parameter.".format(type_str), type_pos
+ f"Value '{type_str}' for random_switch parameter 2 'type' requires a parameter.", type_pos
)
count = None
else:
# Param given
if type_info["param"] == 0:
raise generic.ScriptError(
- "Value '{}' for random_switch parameter 2 'type' should not have a parameter.".format(type_str),
+ f"Value '{type_str}' for random_switch parameter 2 'type' should not have a parameter.",
type_pos,
)
if (
@@ -157,7 +157,7 @@ def parse_randomswitch_type(random_switch):
if random_switch.triggers.value != 0 and not type_info["triggers"]:
raise generic.ScriptError(
- "Triggers may not be set for random_switch feature {:d} and type '{}'. ".format(feature_val, type_str),
+ f"Triggers may not be set for random_switch feature {feature_val} and type '{type_str}'. ",
type_pos,
)
@@ -215,16 +215,16 @@ def parse_randomswitch_dependencies(random_switch, start_bit, bits_available, nr
if act2_to_copy is not None:
if act2_to_copy.randbit != act2.randbit:
msg = (
- "random_switch '{}' cannot be dependent on both '{}' and '{}'"
- " as these are independent of each other."
- ).format(random_switch.name.value, act2_to_copy.name, act2.name)
+ f"random_switch '{random_switch.name.value}' cannot be dependent on"
+ f" both '{act2_to_copy.name}' and '{act2.name}' as these are independent of each other."
+ )
raise generic.ScriptError(msg, random_switch.pos)
if act2_to_copy.nrand != act2.nrand:
msg = (
- "random_switch '{}' cannot be dependent on both '{}' and '{}'"
- " as they don't use the same amount of random data."
- ).format(random_switch.name.value, act2_to_copy.name, act2.name)
+ f"random_switch '{random_switch.name.value}' cannot be dependent on"
+ f" both '{act2_to_copy.name}' and '{act2.name}' as they don't use the same amount of random data."
+ )
raise generic.ScriptError(msg, random_switch.pos)
else:
act2_to_copy = act2
@@ -254,8 +254,8 @@ def parse_randomswitch_dependencies(random_switch, start_bit, bits_available, nr
if possible_mask & (required_mask << randbit) != (required_mask << randbit):
msg = (
"Combination of dependence on and independence from"
- " random_switches is not possible for random_switch '{}'."
- ).format(random_switch.name.value)
+ f" random_switches is not possible for random_switch '{random_switch.name.value}'."
+ )
raise generic.ScriptError(msg, random_switch.pos)
else:
# find a suitable randbit
@@ -292,9 +292,9 @@ def parse_randomswitch(random_switch):
nrand <<= 1
# Verify that enough random data is available
- if min(1 << bits_available, 0x80) < nrand:
- msg = "The maximum sum of all random_switch probabilities is {:d}, encountered {:d}."
- msg = msg.format(min(1 << bits_available, 0x80), total_prob)
+ max_rand_switch_sum = min(1 << bits_available, 0x80)
+ if max_rand_switch_sum < nrand:
+ msg = f"The maximum sum of all random_switch probabilities is {max_rand_switch_sum}, encountered {total_prob}."
raise generic.ScriptError(msg, random_switch.pos)
randbit, nrand = parse_randomswitch_dependencies(random_switch, start_bit, bits_available, nrand)
@@ -344,7 +344,7 @@ def parse_randomswitch(random_switch):
res_prob,
)
offset += res_prob * 2
- comment = "({:d}/{:d}) -> ({:d}/{:d}): ".format(choice.probability.value, total_prob, res_prob, nrand) + comment
+ comment = f"({choice.probability.value}/{total_prob}) -> ({res_prob}/{nrand}): " + comment
random_action2.choices.append(RandomAction2Choice(result, res_prob, comment))
if len(act6.modifications) > 0:
@@ -355,9 +355,7 @@ def parse_randomswitch(random_switch):
random_switch.set_action2(random_action2, feature)
else:
# Create intermediate varaction2 to compute parameter for type 0x84
- varaction2 = action2var.Action2Var(
- feature, "{}@registers".format(random_switch.name.value), random_switch.pos, 0x89
- )
+ varaction2 = action2var.Action2Var(feature, f"{random_switch.name.value}@registers", random_switch.pos, 0x89)
varact2parser = action2var.Varaction2Parser(feature)
varact2parser.parse_expr(count_expr)
varaction2.var_list = varact2parser.var_list
diff --git a/nml/actions/action2real.py b/nml/actions/action2real.py
index 8f73650b1..1c26ec933 100644
--- a/nml/actions/action2real.py
+++ b/nml/actions/action2real.py
@@ -46,9 +46,7 @@ def get_real_action2s(spritegroup, feature):
if feature not in action2.features_sprite_group:
raise generic.ScriptError(
- "Sprite groups that combine sprite sets are not supported for feature '{}'.".format(
- general.feature_name(feature)
- ),
+ f"Sprite groups that combine sprite sets are not supported for feature '{general.feature_name(feature)}'.",
spritegroup.pos,
)
@@ -58,7 +56,7 @@ def get_real_action2s(spritegroup, feature):
spriteset_list.extend([action2.resolve_spritegroup(sg_ref.name) for sg_ref in view.spriteset_list])
if feature == 0x04:
if view.name.value not in ["little", "lots"]:
- raise generic.ScriptError("Unexpected '{}' (list of) sprite set(s).".format(view.name), view.pos)
+ raise generic.ScriptError(f"Unexpected '{view.name}' (list of) sprite set(s).", view.pos)
view.name.value = "loading" if view.name.value == "lots" else "loaded"
actions.extend(action1.add_to_action1(spriteset_list, feature, spritegroup.pos))
@@ -71,9 +69,9 @@ def get_real_action2s(spritegroup, feature):
raise generic.ScriptError("Expected at least a 'lots' (list of) sprite set(s).", spritegroup.pos)
elif feature in (0x05, 0x0B, 0x0D, 0x10):
msg = (
- "Sprite groups for feature {:02X} will not be supported in the future, as they are no longer needed."
- " Directly refer to sprite sets instead."
- ).format(feature)
+ f"Sprite groups for feature {feature:02X} will not be supported in the future,"
+ " as they are no longer needed. Directly refer to sprite sets instead."
+ )
generic.print_warning(generic.Warning.GENERIC, msg, spritegroup.pos)
if view_names != ["default"]:
raise generic.ScriptError("Expected only a 'default' (list of) sprite set(s).", spritegroup.pos)
@@ -97,7 +95,7 @@ def get_real_action2s(spritegroup, feature):
actions.append(
Action2Real(
feature,
- spritegroup.name.value + " - feature {:02X}".format(feature),
+ spritegroup.name.value + f" - feature {feature:02X}",
spritegroup.pos,
loaded_list,
loading_list,
@@ -146,7 +144,7 @@ def create_spriteset_actions(spritegroup):
for feature in spritegroup.feature_set:
if len(spritegroup.used_sprite_sets) != 0 and feature not in action2.features_sprite_group:
raise generic.ScriptError(
- "Directly referring to sprite sets is not possible for feature {:02X}".format(feature), spritegroup.pos
+ f"Directly referring to sprite sets is not possible for feature {feature:02X}", spritegroup.pos
)
for spriteset in spritegroup.used_sprite_sets:
if spriteset.has_action2(feature):
@@ -155,7 +153,7 @@ def create_spriteset_actions(spritegroup):
real_action2 = make_simple_real_action2(
feature,
- spriteset.name.value + " - feature {:02X}".format(feature),
+ spriteset.name.value + f" - feature {feature:02X}",
spritegroup.pos,
action1.get_action1_index(spriteset, feature),
)
diff --git a/nml/actions/action2var.py b/nml/actions/action2var.py
index 128e6237e..b3777f697 100644
--- a/nml/actions/action2var.py
+++ b/nml/actions/action2var.py
@@ -15,7 +15,7 @@
from nml import expression, generic, global_constants, nmlop
from nml.actions import action2, action2real, action2var_variables, action4, action6, actionD
-from nml.ast import general, switch, spriteblock
+from nml.ast import general, spriteblock, switch
class Action2Var(action2.Action2):
@@ -97,7 +97,7 @@ def write(self, file):
else:
size += var.get_size()
- regs = ["{} : register {:X}".format(reg.name, reg.register) for reg in self.param_registers]
+ regs = [f"{reg.name} : register {reg.register:X}" for reg in self.param_registers]
self.write_sprite_start(file, size, regs)
file.print_bytex(self.type_byte)
file.newline()
@@ -198,11 +198,9 @@ class VarAction2ProcCallVar(VarAction2Var):
def __init__(self, sg_ref):
if not sg_ref.act2:
if not isinstance(action2.resolve_spritegroup(sg_ref.name), (switch.Switch, switch.RandomSwitch)):
- raise generic.ScriptError(
- "Block with name '{}' is not a valid procedure".format(sg_ref.name), sg_ref.pos
- )
+ raise generic.ScriptError(f"Block with name '{sg_ref.name}' is not a valid procedure", sg_ref.pos)
if not sg_ref.is_procedure:
- raise generic.ScriptError("Unexpected identifier encountered: '{}'".format(sg_ref.name), sg_ref.pos)
+ raise generic.ScriptError(f"Unexpected identifier encountered: '{sg_ref.name}'", sg_ref.pos)
VarAction2Var.__init__(self, 0x7E, 0, 0, comment=str(sg_ref))
# Reference to the called action2
self.sg_ref = sg_ref
@@ -475,7 +473,7 @@ def preprocess_storageop(self, expr):
assert isinstance(expr, expression.StorageOp)
if expr.info["perm"] and not self.var_scope.has_persistent_storage:
raise generic.ScriptError(
- "Persistent storage is not supported for feature '{}'".format(self.var_scope.name),
+ f"Persistent storage is not supported for feature '{self.var_scope.name}'",
expr.pos,
)
@@ -576,7 +574,7 @@ def parse_binop(self, expr):
self.in_store_op = True
if (
- isinstance(expr.expr2, (expression.ConstantNumeric, expression.Variable))
+ isinstance(expr.expr2, (expression.ConstantNumeric, expression.Variable)) # noqa:SIM101 # keep related types together
or isinstance(expr.expr2, (VarAction2LoadTempVar, VarAction2LoadCallParam))
or (isinstance(expr.expr2, expression.Parameter) and isinstance(expr.expr2.num, expression.ConstantNumeric))
or (isinstance(expr.expr2, expression.StorageOp) and expr.expr2.name == "LOAD_TEMP")
@@ -844,7 +842,7 @@ def create_ternary_action(guard, expr_true, expr_false, action_list, feature, va
act6 = action6.Action6()
global return_action_id
- name = "@ternary_action_{:d}".format(return_action_id)
+ name = f"@ternary_action_{return_action_id}"
varaction2 = Action2Var(feature, name, guard.pos, var_range)
return_action_id += 1
@@ -960,9 +958,7 @@ def get_failed_cb_result(feature, action_list, parent_action, pos):
# Normal action2
act1_actions, act1_index = action1.make_cb_failure_action1(feature)
action_list.extend(act1_actions)
- act2 = action2real.make_simple_real_action2(
- feature, "@CB_FAILED_REAL{:02X}".format(feature), pos, act1_index
- )
+ act2 = action2real.make_simple_real_action2(feature, f"@CB_FAILED_REAL{feature:02X}", pos, act1_index)
action_list.append(act2)
# Create varaction2, to choose between returning graphics and 0, depending on CB
@@ -971,7 +967,7 @@ def get_failed_cb_result(feature, action_list, parent_action, pos):
expression.Variable(expression.ConstantNumeric(0x0C), mask=expression.ConstantNumeric(0xFFFF))
)
- varaction2 = Action2Var(feature, "@CB_FAILED{:02X}".format(feature), pos, 0x89)
+ varaction2 = Action2Var(feature, f"@CB_FAILED{feature:02X}", pos, 0x89)
varaction2.var_list = varact2parser.var_list
varaction2.ranges.append(
@@ -1019,10 +1015,9 @@ def parse_sg_ref_result(result, action_list, parent_action, var_range):
target = action2.resolve_spritegroup(result.name) if not result.act2 else None
if parent_action.feature not in action2.features_sprite_layout and isinstance(target, spriteblock.SpriteLayout):
+ parent_name = general.feature_name(parent_action.feature)
raise generic.ScriptError(
- "SpriteLayout '{}' is not a valid return value for feature '{}'".format(
- result.name, general.feature_name(parent_action.feature)
- ),
+ f"SpriteLayout '{result.name}' is not a valid return value for feature '{parent_name}'",
result.pos,
)
@@ -1052,7 +1047,7 @@ def parse_sg_ref_result(result, action_list, parent_action, var_range):
action_list.append(extra_act6)
global return_action_id
- name = "@return_action_{:d}".format(return_action_id)
+ name = f"@return_action_{return_action_id}"
varaction2 = Action2Var(parent_action.feature, name, result.pos, var_range)
return_action_id += 1
varaction2.var_list = varact2parser.var_list
@@ -1119,23 +1114,23 @@ def parse_result(value, action_list, act6, offset, parent_action, none_result, v
result = parse_sg_ref_result(value, action_list, parent_action, var_range)
comment = result.name.value + ";"
elif isinstance(value, expression.ConstantNumeric):
- comment = "return {:d};".format(value.value)
+ comment = f"return {value.value};"
result = value
if not (-16384 <= value.value <= 32767):
msg = (
"Callback results are limited to -16384..16383 (when the result is a signed number)"
- " or 0..32767 (unsigned), encountered {:d}."
- ).format(value.value)
+ f" or 0..32767 (unsigned), encountered {value.value}."
+ )
raise generic.ScriptError(msg, value.pos)
elif isinstance(value, expression.String):
- comment = "return {};".format(str(value))
+ comment = f"return {value};"
str_id, actions = action4.get_string_action4s(0, 0xD0, value)
action_list.extend(actions)
result = expression.ConstantNumeric(str_id - 0xD000 + 0x8000)
elif value.supported_by_actionD(False):
tmp_param, tmp_param_actions = actionD.get_tmp_parameter(nmlop.OR(value, 0x8000).reduce())
- comment = "return param[{:d}];".format(tmp_param)
+ comment = f"return param[{tmp_param}];"
action_list.extend(tmp_param_actions)
for i in range(repeat_result):
act6.modify_bytes(tmp_param, 2, offset + 2 * i)
@@ -1143,12 +1138,12 @@ def parse_result(value, action_list, act6, offset, parent_action, none_result, v
else:
global return_action_id
extra_actions, result = create_return_action(
- value, parent_action.feature, "@return_action_{:d}".format(return_action_id), var_range
+ value, parent_action.feature, f"@return_action_{return_action_id}", var_range
)
return_action_id += 1
action2.add_ref(result, parent_action)
action_list.extend(extra_actions)
- comment = "return {}".format(value)
+ comment = f"return {value}"
return (result, comment)
diff --git a/nml/actions/action2var_variables.py b/nml/actions/action2var_variables.py
index 0a30dc615..0c9218dbd 100644
--- a/nml/actions/action2var_variables.py
+++ b/nml/actions/action2var_variables.py
@@ -15,6 +15,7 @@
from nml import expression, generic, nmlop
+
def default_60xvar(name, args, pos, info):
"""
Function to convert arguments into a variable parameter.
@@ -38,46 +39,53 @@ def default_60xvar(name, args, pos, info):
@rtype: C{tuple} of (L{Expression}, C{list} C{tuple} of (C{int}, L{Expression}))
"""
if len(args) != 1:
- raise generic.ScriptError("'{}'() requires one argument, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires one argument, encountered {len(args)}", pos)
return (args[0], [])
# Some commonly used functions that apply some modification to the raw variable value
# To pass extra parameters, lambda calculus may be used
+
def value_sign_extend(var, info):
- #r = (x ^ m) - m; with m being (1 << (num_bits -1))
+ # r = (x ^ m) - m; with m being (1 << (num_bits -1))
m = expression.ConstantNumeric(1 << (info['size'] - 1))
return nmlop.SUB(nmlop.XOR(var, m), m)
+
def value_mul_div(mul, div):
return lambda var, info: nmlop.DIV(nmlop.MUL(var, mul), div)
+
def value_add_constant(const):
return lambda var, info: nmlop.ADD(var, const)
+
def value_equals(const):
return lambda var, info: nmlop.CMP_EQ(var, const)
# Commonly used functions to let a variable accept an (x, y)-offset as parameters
+
def tile_offset(name, args, pos, info, min, max):
if len(args) != 2:
- raise generic.ScriptError("'{}'() requires 2 arguments, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires 2 arguments, encountered {len(args)}", pos)
for arg in args:
if isinstance(arg, expression.ConstantNumeric):
- generic.check_range(arg.value, min, max, "Argument of '{}'".format(name), arg.pos)
+ generic.check_range(arg.value, min, max, f"Argument of '{name}'", arg.pos)
x = nmlop.AND(args[0], 0xF)
y = nmlop.AND(args[1], 0xF)
# Shift y left by four
y = nmlop.SHIFT_LEFT(y, 4)
param = nmlop.ADD(x, y)
- #Make sure to reduce the result
- return ( param.reduce(), [] )
+ # Make sure to reduce the result
+ return (param.reduce(), [])
+
def signed_tile_offset(name, args, pos, info):
return tile_offset(name, args, pos, info, -8, 7)
+
def unsigned_tile_offset(name, args, pos, info):
return tile_offset(name, args, pos, info, 0, 15)
@@ -85,6 +93,7 @@ def unsigned_tile_offset(name, args, pos, info):
# Global variables, usable for all features
#
+
varact2_globalvars = {
'current_month' : {'var': 0x02, 'start': 0, 'size': 8},
'current_day_of_month' : {'var': 0x02, 'start': 8, 'size': 5},
@@ -114,10 +123,10 @@ def unsigned_tile_offset(name, args, pos, info):
'grfid' : {'var': 0x25, 'start': 0, 'size': 32},
'position_in_consist' : {'var': 0x40, 'start': 0, 'size': 8},
'position_in_consist_from_end' : {'var': 0x40, 'start': 8, 'size': 8},
- 'num_vehs_in_consist' : {'var': 0x40, 'start': 16, 'size': 8, 'value_function': value_add_constant(1)}, # Zero-based, add 1 to make sane
+ 'num_vehs_in_consist' : {'var': 0x40, 'start': 16, 'size': 8, 'value_function': value_add_constant(1)}, # Zero-based, add 1 to make sane
'position_in_vehid_chain' : {'var': 0x41, 'start': 0, 'size': 8},
'position_in_vehid_chain_from_end' : {'var': 0x41, 'start': 8, 'size': 8},
- 'num_vehs_in_vehid_chain' : {'var': 0x41, 'start': 16, 'size': 8}, # One-based, already sane
+ 'num_vehs_in_vehid_chain' : {'var': 0x41, 'start': 16, 'size': 8}, # One-based, already sane
'cargo_classes_in_consist' : {'var': 0x42, 'start': 0, 'size': 8},
'most_common_cargo_type' : {'var': 0x42, 'start': 8, 'size': 8},
'most_common_cargo_subtype' : {'var': 0x42, 'start': 16, 'size': 8},
@@ -173,8 +182,8 @@ def unsigned_tile_offset(name, args, pos, info):
varact2vars_trains = {
**varact2vars_vehicles,
- #0x4786 / 0x10000 is an approximation of 3.5790976, the conversion factor
- #for train speed
+ # 0x4786 / 0x10000 is an approximation of 3.5790976, the conversion factor
+ # for train speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x4786, 0x10000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x4786, 0x10000)},
'current_railtype' : {'var': 0x4A, 'start': 0, 'size': 8},
@@ -184,8 +193,8 @@ def unsigned_tile_offset(name, args, pos, info):
varact2vars_roadvehs = {
**varact2vars_vehicles,
- #0x23C3 / 0x10000 is an approximation of 7.1581952, the conversion factor
- #for road vehicle speed
+ # 0x23C3 / 0x10000 is an approximation of 7.1581952, the conversion factor
+ # for road vehicle speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_roadtype' : {'var': 0x4A, 'start': 0, 'size': 8},
@@ -196,8 +205,8 @@ def unsigned_tile_offset(name, args, pos, info):
varact2vars_ships = {
**varact2vars_vehicles,
- #0x23C3 / 0x10000 is an approximation of 7.1581952, the conversion factor
- #for ship speed
+ # 0x23C3 / 0x10000 is an approximation of 7.1581952, the conversion factor
+ # for ship speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_max_speed' : {'var': 0x4C, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
@@ -206,9 +215,9 @@ def unsigned_tile_offset(name, args, pos, info):
varact2vars_aircraft = {
**varact2vars_vehicles,
- #0x3939 / 0x1000 is an approximation of 0.279617, the conversion factor
- #Note that the denominator has one less zero here!
- #for aircraft speed
+ # 0x3939 / 0x1000 is an approximation of 0.279617, the conversion factor
+ # Note that the denominator has one less zero here!
+ # for aircraft speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x3939, 0x1000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x3939, 0x1000)},
'current_max_speed' : {'var': 0x4C, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x3939, 0x1000)},
@@ -216,51 +225,56 @@ def unsigned_tile_offset(name, args, pos, info):
'vehicle_is_in_depot' : {'var': 0xE6, 'start': 0, 'size': 8, 'value_function': value_equals(0)},
}
+
def signed_byte_parameter(name, args, pos, info):
# Convert to a signed byte by AND-ing with 0xFF
if len(args) != 1:
- raise generic.ScriptError("{}() requires one argument, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"{name}() requires one argument, encountered {len(args)}", pos)
if isinstance(args[0], expression.ConstantNumeric):
- generic.check_range(args[0].value, -128, 127, "parameter of {}()".format(name), pos)
+ generic.check_range(args[0].value, -128, 127, f"parameter of {name}()", pos)
ret = nmlop.AND(args[0], 0xFF, pos).reduce()
return (ret, [])
+
def vehicle_railtype(name, args, pos, info):
return (expression.functioncall.builtin_resolve_typelabel(name, args, pos, table_name="railtype"), [])
+
def vehicle_roadtype(name, args, pos, info):
return (expression.functioncall.builtin_resolve_typelabel(name, args, pos, table_name="roadtype"), [])
+
def vehicle_tramtype(name, args, pos, info):
return (expression.functioncall.builtin_resolve_typelabel(name, args, pos, table_name="tramtype"), [])
+
varact2vars60x_vehicles = {
'count_veh_id' : {'var': 0x60, 'start': 0, 'size': 8},
- 'other_veh_curv_info' : {'var': 0x62, 'start': 0, 'size': 4, 'param_function':signed_byte_parameter, 'value_function':value_sign_extend},
- 'other_veh_is_hidden' : {'var': 0x62, 'start': 7, 'size': 1, 'param_function':signed_byte_parameter},
- 'other_veh_x_offset' : {'var': 0x62, 'start': 8, 'size': 8, 'param_function':signed_byte_parameter, 'value_function':value_sign_extend},
- 'other_veh_y_offset' : {'var': 0x62, 'start': 16, 'size': 8, 'param_function':signed_byte_parameter, 'value_function':value_sign_extend},
- 'other_veh_z_offset' : {'var': 0x62, 'start': 24, 'size': 8, 'param_function':signed_byte_parameter, 'value_function':value_sign_extend},
+ 'other_veh_curv_info' : {'var': 0x62, 'start': 0, 'size': 4, 'param_function': signed_byte_parameter, 'value_function': value_sign_extend},
+ 'other_veh_is_hidden' : {'var': 0x62, 'start': 7, 'size': 1, 'param_function': signed_byte_parameter},
+ 'other_veh_x_offset' : {'var': 0x62, 'start': 8, 'size': 8, 'param_function': signed_byte_parameter, 'value_function': value_sign_extend},
+ 'other_veh_y_offset' : {'var': 0x62, 'start': 16, 'size': 8, 'param_function': signed_byte_parameter, 'value_function': value_sign_extend},
+ 'other_veh_z_offset' : {'var': 0x62, 'start': 24, 'size': 8, 'param_function': signed_byte_parameter, 'value_function': value_sign_extend},
}
varact2vars60x_trains = {
**varact2vars60x_vehicles,
# Var 0x63 bit 0 is only useful when testing multiple bits at once. On its own it is already covered by railtype_available().
- 'tile_supports_railtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function':vehicle_railtype},
- 'tile_powers_railtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function':vehicle_railtype},
- 'tile_is_railtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function':vehicle_railtype},
+ 'tile_supports_railtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function': vehicle_railtype},
+ 'tile_powers_railtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function': vehicle_railtype},
+ 'tile_is_railtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function': vehicle_railtype},
}
varact2vars60x_roadvehs = {
**varact2vars60x_vehicles,
# Var 0x63 bit 0 is only useful when testing multiple bits at once. On its own it is already covered by road/tramtype_available().
- 'tile_supports_roadtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function':vehicle_roadtype},
- 'tile_supports_tramtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function':vehicle_tramtype},
- 'tile_powers_roadtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function':vehicle_roadtype},
- 'tile_powers_tramtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function':vehicle_tramtype},
- 'tile_is_roadtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function':vehicle_roadtype},
- 'tile_is_tramtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function':vehicle_tramtype},
+ 'tile_supports_roadtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function': vehicle_roadtype},
+ 'tile_supports_tramtype' : {'var': 0x63, 'start': 1, 'size': 1, 'param_function': vehicle_tramtype},
+ 'tile_powers_roadtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function': vehicle_roadtype},
+ 'tile_powers_tramtype' : {'var': 0x63, 'start': 2, 'size': 1, 'param_function': vehicle_tramtype},
+ 'tile_is_roadtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function': vehicle_roadtype},
+ 'tile_is_tramtype' : {'var': 0x63, 'start': 3, 'size': 1, 'param_function': vehicle_tramtype},
}
#
@@ -271,7 +285,7 @@ def vehicle_tramtype(name, args, pos, info):
varact2vars_base_stations = {
'random_bits_station' : {'var': 0x5F, 'start': 8, 'size': 16},
# Var 48 doesn't work with newcargos, do not use
- 'had_vehicle_of_type' : {'var': 0x8A, 'start': 1, 'size': 5}, # Only read bits 1-5
+ 'had_vehicle_of_type' : {'var': 0x8A, 'start': 1, 'size': 5}, # Only read bits 1-5
'is_waypoint' : {'var': 0x8A, 'start': 6, 'size': 1},
'facilities' : {'var': 0xF0, 'start': 0, 'size': 8},
'airport_type' : {'var': 0xF1, 'start': 0, 'size': 8},
@@ -324,17 +338,19 @@ def vehicle_tramtype(name, args, pos, info):
(2, False) : 0x49,
}
+
def platform_info_param(name, args, pos, info):
if len(args) != 1:
- raise generic.ScriptError("'{}'() requires one argument, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires one argument, encountered {len(args)}", pos)
if (not isinstance(args[0], expression.ConstantNumeric)) or args[0].value not in (0, 1, 2):
- raise generic.ScriptError("Invalid argument for '{}'(), must be one of PLATFORM_SAME_XXX.".format(name), pos)
+ raise generic.ScriptError(f"Invalid argument for '{name}'(), must be one of PLATFORM_SAME_XXX.", pos)
is_middle = 'middle' in info and info['middle']
if is_middle and args[0].value == 2:
- raise generic.ScriptError("Invalid argument for '{}'(), PLATFORM_SAME_DIRECTION is not supported here.".format(name), pos)
+ raise generic.ScriptError(f"Invalid argument for '{name}'(), PLATFORM_SAME_DIRECTION is not supported here.", pos)
# Temporarily store variable number in the param, this will be fixed in the value_function
- return (expression.ConstantNumeric(mapping_platform_param[(args[0].value, is_middle)]), [])
+ return (expression.ConstantNumeric(mapping_platform_param[args[0].value, is_middle]), [])
+
def platform_info_fix_var(var, info):
# Variable to use was temporarily stored in the param
@@ -343,6 +359,7 @@ def platform_info_fix_var(var, info):
var.param = None
return var
+
varact2vars60x_stations = {
**varact2vars60x_base_stations,
'nearby_tile_animation_frame' : {'var': 0x66, 'start': 0, 'size': 32, 'param_function': signed_tile_offset},
@@ -368,9 +385,9 @@ def platform_info_fix_var(var, info):
'platform_position_from_end' : {'var': 0x00, 'start': 4, 'size': 4, 'param_function': platform_info_param, 'value_function': platform_info_fix_var},
'platform_number_from_start' : {'var': 0x00, 'start': 8, 'size': 4, 'param_function': platform_info_param, 'value_function': platform_info_fix_var},
'platform_number_from_end' : {'var': 0x00, 'start': 12, 'size': 4, 'param_function': platform_info_param, 'value_function': platform_info_fix_var},
- 'platform_position_from_middle' : {'var': 0x00, 'start': 0, 'size': 4, 'param_function': platform_info_param, 'middle': True, # 'middle' is used by platform_info_param
+ 'platform_position_from_middle' : {'var': 0x00, 'start': 0, 'size': 4, 'param_function': platform_info_param, 'middle': True, # 'middle' is used by platform_info_param
'value_function': lambda var, info: value_sign_extend(platform_info_fix_var(var, info), info)},
- 'platform_number_from_middle' : {'var': 0x00, 'start': 4, 'size': 4, 'param_function': platform_info_param, 'middle': True, # 'middle' is used by platform_info_param
+ 'platform_number_from_middle' : {'var': 0x00, 'start': 4, 'size': 4, 'param_function': platform_info_param, 'middle': True, # 'middle' is used by platform_info_param
'value_function': lambda var, info: value_sign_extend(platform_info_fix_var(var, info), info)},
}
@@ -394,6 +411,7 @@ def platform_info_fix_var(var, info):
# Houses (feature 0x07)
#
+
def house_same_class(var, info):
# Just using var 44 fails for non-north house tiles, as these have no class
# Therefore work around it using var 61
@@ -413,8 +431,8 @@ def house_same_class(var, info):
'terrain_type' : {'var': 0x43, 'start': 0, 'size': 8},
'same_house_count_town' : {'var': 0x44, 'start': 0, 'size': 8},
'same_house_count_map' : {'var': 0x44, 'start': 8, 'size': 8},
- 'same_class_count_town' : {'var': 0xFF, 'start': 16, 'size': 8, 'value_function': house_same_class}, # 'var' is unused
- 'same_class_count_map' : {'var': 0xFF, 'start': 24, 'size': 8, 'value_function': house_same_class}, # 'var' is unused
+ 'same_class_count_town' : {'var': 0xFF, 'start': 16, 'size': 8, 'value_function': house_same_class}, # 'var' is unused
+ 'same_class_count_map' : {'var': 0xFF, 'start': 24, 'size': 8, 'value_function': house_same_class}, # 'var' is unused
'generating_town' : {'var': 0x45, 'start': 0, 'size': 1},
'animation_frame' : {'var': 0x46, 'start': 0, 'size': 8},
'x_coordinate' : {'var': 0x47, 'start': 0, 'size': 16},
@@ -427,16 +445,17 @@ def house_same_class(var, info):
'house_type_id' : {'var': 0x7D, 'start': 24, 'size': 8, 'param': 0xFF},
}
+
def cargo_accepted_nearby(name, args, pos, info):
# cargo_accepted_nearby(cargo[, xoffset, yoffset])
if len(args) not in (1, 3):
- raise generic.ScriptError("{}() requires 1 or 3 arguments, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"{name}() requires 1 or 3 arguments, encountered {len(args)}", pos)
if len(args) > 1:
offsets = args[1:3]
for i, offs in enumerate(offsets[:]):
if isinstance(offs, expression.ConstantNumeric):
- generic.check_range(offs.value, -128, 127, "{}-parameter {:d} '{}offset'".format(name, i + 1, "x" if i == 0 else "y"), pos)
+ generic.check_range(offs.value, -128, 127, f"{name}-parameter {i + 1} '{'x' if i == 0 else 'y'}offset'", pos)
offsets[i] = nmlop.AND(offs, 0xFF, pos).reduce()
# Register 0x100 should be set to xoffset | (yoffset << 8)
reg100 = nmlop.OR(nmlop.MUL(offsets[1], 256, pos), offsets[0]).reduce()
@@ -445,15 +464,16 @@ def cargo_accepted_nearby(name, args, pos, info):
return (args[0], [(0x100, reg100)])
+
def nearest_house_matching_criterion(name, args, pos, info):
# nearest_house_matching_criterion(radius, criterion)
# parameter is radius | (criterion << 6)
if len(args) != 2:
- raise generic.ScriptError("{}() requires 2 arguments, encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"{name}() requires 2 arguments, encountered {len(args)}", pos)
if isinstance(args[0], expression.ConstantNumeric):
- generic.check_range(args[0].value, 1, 63, "{}()-parameter 1 'radius'".format(name), pos)
+ generic.check_range(args[0].value, 1, 63, f"{name}()-parameter 1 'radius'", pos)
if isinstance(args[1], expression.ConstantNumeric) and args[1].value not in (0, 1, 2):
- raise generic.ScriptError("Invalid value for {}()-parameter 2 'criterion'".format(name), pos)
+ raise generic.ScriptError(f"Invalid value for {name}()-parameter 2 'criterion'", pos)
radius = nmlop.AND(args[0], 0x3F, pos)
criterion = nmlop.AND(args[1], 0x03, pos)
@@ -461,6 +481,7 @@ def nearest_house_matching_criterion(name, args, pos, info):
retval = nmlop.OR(criterion, radius).reduce()
return (retval, [])
+
varact2vars60x_houses = {
'old_house_count_town' : {'var': 0x60, 'start': 0, 'size': 8},
'old_house_count_map' : {'var': 0x60, 'start': 8, 'size': 8},
@@ -558,9 +579,10 @@ def nearest_house_matching_criterion(name, args, pos, info):
'last_accept_date' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_add_constant(701265)},
}
+
def industry_count(name, args, pos, info):
if len(args) < 1 or len(args) > 2:
- raise generic.ScriptError("'{}'() requires between 1 and 2 argument(s), encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires between 1 and 2 argument(s), encountered {len(args)}", pos)
grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 1 else args[1]
extra_params = [(0x100, grfid)]
@@ -570,29 +592,32 @@ def industry_count(name, args, pos, info):
def industry_layout_count(name, args, pos, info):
if len(args) < 2 or len(args) > 3:
- raise generic.ScriptError("'{}'() requires between 2 and 3 argument(s), encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires between 2 and 3 argument(s), encountered {len(args)}", pos)
grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 2 else args[2]
extra_params = []
- extra_params.append( (0x100, grfid) )
- extra_params.append( (0x101, nmlop.AND(args[1], 0xFF).reduce()) )
+ extra_params.append((0x100, grfid))
+ extra_params.append((0x101, nmlop.AND(args[1], 0xFF).reduce()))
return (args[0], extra_params)
+
def industry_town_count(name, args, pos, info):
if len(args) < 1 or len(args) > 2:
- raise generic.ScriptError("'{}'() requires between 1 and 2 argument(s), encountered {:d}".format(name, len(args)), pos)
+ raise generic.ScriptError(f"'{name}'() requires between 1 and 2 argument(s), encountered {len(args)}", pos)
grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 1 else args[1]
extra_params = []
- extra_params.append( (0x100, grfid) )
- extra_params.append( (0x101, expression.ConstantNumeric(0x0100)) )
+ extra_params.append((0x100, grfid))
+ extra_params.append((0x101, expression.ConstantNumeric(0x0100)))
return (args[0], extra_params)
+
def industry_cargotype(name, args, pos, info):
return (expression.functioncall.builtin_resolve_typelabel(name, args, pos, table_name="cargotype"), [])
+
varact2vars60x_industries = {
'nearby_tile_industry_tile_id' : {'var': 0x60, 'start': 0, 'size': 16, 'param_function': unsigned_tile_offset},
'nearby_tile_random_bits' : {'var': 0x61, 'start': 0, 'size': 8, 'param_function': unsigned_tile_offset},
@@ -803,17 +828,17 @@ def industry_cargotype(name, args, pos, info):
'has_road' : {'var': 0x43, 'start': 0, 'size': 32, 'value_function': lambda var, info: nmlop.CMP_NEQ(var, 0xFFFFFFFF)},
'has_tram' : {'var': 0x44, 'start': 0, 'size': 32, 'value_function': lambda var, info: nmlop.CMP_NEQ(var, 0xFFFFFFFF)},
- 'road_type' : {'var': 0x43, 'start': 0, 'size': 8}, # The roadtype of this tile
- 'tram_type' : {'var': 0x44, 'start': 0, 'size': 8}, # The tramtype of this tile
+ 'road_type' : {'var': 0x43, 'start': 0, 'size': 8}, # The roadtype of this tile
+ 'tram_type' : {'var': 0x44, 'start': 0, 'size': 8}, # The tramtype of this tile
'town_manhattan_dist' : {'var': 0x45, 'start': 0, 'size': 16},
'town_zone' : {'var': 0x45, 'start': 16, 'size': 8},
'town_euclidean_dist' : {'var': 0x46, 'start': 0, 'size': 32},
- 'company_num' : {'var': 0x47, 'start': 0, 'size': 8}, # 0..14 company number
- 'company_type' : {'var': 0x47, 'start': 16, 'size': 2}, # PLAYERTYPE_HUMAN, PLAYERTYPE_AI etc.
- 'company_colour1' : {'var': 0x47, 'start': 24, 'size': 4}, # COLOUR_XXX. See https://newgrf-specs.tt-wiki.net/wiki/NML:List_of_default_colour_translation_palettes#Company_colour_helper_functions
- 'company_colour2' : {'var': 0x47, 'start': 28, 'size': 4}, # Same as above
+ 'company_num' : {'var': 0x47, 'start': 0, 'size': 8}, # 0..14 company number
+ 'company_type' : {'var': 0x47, 'start': 16, 'size': 2}, # PLAYERTYPE_HUMAN, PLAYERTYPE_AI etc.
+ 'company_colour1' : {'var': 0x47, 'start': 24, 'size': 4}, # COLOUR_XXX. See https://newgrf-specs.tt-wiki.net/wiki/NML:List_of_default_colour_translation_palettes#Company_colour_helper_functions
+ 'company_colour2' : {'var': 0x47, 'start': 28, 'size': 4}, # Same as above
'animation_frame' : {'var': 0x49, 'start': 0, 'size': 8},
@@ -848,6 +873,7 @@ def industry_cargotype(name, args, pos, info):
'nearby_tile_road_stop_id' : {'var': 0x6B, 'start': 0, 'size': 16, 'param_function': signed_tile_offset},
}
+
class VarAct2Scope:
def __init__(self, name, vars_normal, vars_60x, has_persistent_storage=False):
self.name = name
diff --git a/nml/actions/action3.py b/nml/actions/action3.py
index 8fcb599fe..3e2ae9248 100644
--- a/nml/actions/action3.py
+++ b/nml/actions/action3.py
@@ -129,7 +129,7 @@ def create_intermediate_varaction2(feature, varact2parser, mapping, default, pos
for mod in varact2parser.mods:
act6.modify_bytes(mod.param, mod.size, mod.offset + 4)
- name = expression.Identifier("@action3_{:d}".format(action2_id))
+ name = expression.Identifier(f"@action3_{action2_id}")
action2_id += 1
varaction2 = action2var.Action2Var(feature, name.value, pos, 0x89)
varaction2.var_list = varact2parser.var_list
@@ -319,7 +319,7 @@ def parse_graphics_block_single_id(
cb_table = action3_callbacks.callbacks[feature]
if cb_name in cb_table:
if cb_name in seen_callbacks:
- raise generic.ScriptError("Callback '{}' is defined multiple times.".format(cb_name), cargo_id.pos)
+ raise generic.ScriptError(f"Callback '{cb_name}' is defined multiple times.", cargo_id.pos)
seen_callbacks.add(cb_name)
info_list = cb_table[cb_name]
@@ -336,7 +336,7 @@ def parse_graphics_block_single_id(
# Not a callback, but an alias for a certain cargo type
if info["num"] in cargo_gfx:
raise generic.ScriptError(
- "Graphics for '{}' are defined multiple times.".format(cb_name), cargo_id.pos
+ f"Graphics for '{cb_name}' are defined multiple times.", cargo_id.pos
)
cargo_gfx[info["num"]] = graphics.result.value
elif info["type"] == "cb":
@@ -358,7 +358,7 @@ def parse_graphics_block_single_id(
layouts = (var10map, registers_ref)
else:
raise generic.ScriptError(
- "'{}' must be an array of even length, or the ID of a station".format(cb_name),
+ f"'{cb_name}' must be an array of even length, or the ID of a station",
cargo_id.pos,
)
prepend_action_list.extend(actions)
@@ -369,7 +369,7 @@ def parse_graphics_block_single_id(
or len(graphics.result.value.values) > 6
):
raise generic.ScriptError(
- "'{}' must be an array of at most 6 elements".format(cb_name), cargo_id.pos
+ f"'{cb_name}' must be an array of at most 6 elements", cargo_id.pos
)
custom_spritesets = graphics.result.value.values
elif info["type"] == "prepare_layout":
@@ -394,15 +394,13 @@ def parse_graphics_block_single_id(
"Associating graphics with a specific cargo is possible only for vehicles and stations.", cargo_id.pos
)
if cargo_id.value in cargo_gfx:
- raise generic.ScriptError(
- "Graphics for cargo {:d} are defined multiple times.".format(cargo_id.value), cargo_id.pos
- )
+ raise generic.ScriptError(f"Graphics for cargo {cargo_id.value} are defined multiple times.", cargo_id.pos)
cargo_gfx[cargo_id.value] = graphics.result.value
if graphics_block.default_graphics is not None:
if "default" not in action3_callbacks.callbacks[feature]:
raise generic.ScriptError(
- "Default graphics may not be defined for this feature (0x{:02X}).".format(feature),
+ f"Default graphics may not be defined for this feature (0x{feature:02X}).",
graphics_block.default_graphics.pos,
)
if None in cargo_gfx:
@@ -429,7 +427,7 @@ def parse_graphics_block_single_id(
elif prepare_layout:
if not isinstance(prepare_layout, expression.SpriteGroupRef):
actions, prepare_layout = action2var.create_return_action(
- prepare_layout, feature, "Station Layout@prepare - Id {:02X}".format(id.value), 0x89
+ prepare_layout, feature, f"Station Layout@prepare - Id {id.value:02X}", 0x89
)
prepend_action_list.extend(actions)
varact2parser.parse(prepare_layout)
diff --git a/nml/actions/action3_callbacks.py b/nml/actions/action3_callbacks.py
index e88ec4bf8..c11025857 100644
--- a/nml/actions/action3_callbacks.py
+++ b/nml/actions/action3_callbacks.py
@@ -29,12 +29,12 @@
general_vehicle_cbs = {
'default' : {'type': 'cargo', 'num': None},
'purchase' : {'type': 'cargo', 'num': 0xFF},
- 'random_trigger' : {'type': 'cb', 'num': 0x01}, # Almost undocumented, but really necessary!
+ 'random_trigger' : {'type': 'cb', 'num': 0x01}, # Almost undocumented, but really necessary!
'loading_speed' : {'type': 'cb', 'num': 0x36, 'var10': 0x07},
'cargo_subtype_text' : {'type': 'cb', 'num': 0x19, 'flag_bit': 5},
'additional_text' : {'type': 'cb', 'num': 0x23, 'purchase': 2},
- 'colour_mapping' : {'type': 'cb', 'num': 0x2D, 'flag_bit':6, 'purchase': 'purchase_colour_mapping'},
- 'purchase_colour_mapping' : {'type': 'cb', 'num': 0x2D, 'flag_bit':6, 'purchase': 2},
+ 'colour_mapping' : {'type': 'cb', 'num': 0x2D, 'flag_bit': 6, 'purchase': 'purchase_colour_mapping'},
+ 'purchase_colour_mapping' : {'type': 'cb', 'num': 0x2D, 'flag_bit': 6, 'purchase': 2},
'start_stop' : {'type': 'cb', 'num': 0x31},
'every_32_days' : {'type': 'cb', 'num': 0x32},
'sound_effect' : {'type': 'cb', 'num': 0x33, 'flag_bit': 7},
@@ -43,19 +43,21 @@
'refit' : {'type': 'cb', 'num': 0x163, "purchase": 2},
}
+
# Function to convert vehicle length to the actual property value, which is (8 - length)
def vehicle_length(value):
return nmlop.SUB(8, value)
+
# Trains
callbacks[0x00] = {
'visual_effect_and_powered' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
'effect_spawn_model_and_powered' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
'length' : {'type': 'cb', 'num': 0x36, 'var10': 0x21, 'value_function': vehicle_length},
- 'cargo_capacity' : [ {'type': 'cb', 'num': 0x15, 'flag_bit': 3},
+ 'cargo_capacity' : [{'type': 'cb', 'num': 0x15, 'flag_bit': 3},
{'type': 'cb', 'num': 0x36, 'var10': 0x14, 'purchase': 'purchase_cargo_capacity'}],
'purchase_cargo_capacity' : {'type': 'cb', 'num': 0x36, 'var10': 0x14, 'purchase': 2},
- 'articulated_part' : {'type': 'cb', 'num': 0x16, 'flag_bit': 4, 'purchase': 1}, # Don't add separate purchase CB here
+ 'articulated_part' : {'type': 'cb', 'num': 0x16, 'flag_bit': 4, 'purchase': 1}, # Don't add separate purchase CB here
'can_attach_wagon' : {'type': 'cb', 'num': 0x1D},
'speed' : {'type': 'cb', 'num': 0x36, 'var10': 0x09, 'purchase': 'purchase_speed'},
'purchase_speed' : {'type': 'cb', 'num': 0x36, 'var10': 0x09, 'purchase': 2},
@@ -81,10 +83,10 @@ def vehicle_length(value):
'visual_effect' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
'effect_spawn_model' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
'length' : {'type': 'cb', 'num': 0x36, 'var10': 0x23, 'value_function': vehicle_length},
- 'cargo_capacity' : [ {'type': 'cb', 'num': 0x15, 'flag_bit': 3},
+ 'cargo_capacity' : [{'type': 'cb', 'num': 0x15, 'flag_bit': 3},
{'type': 'cb', 'num': 0x36, 'var10': 0x0F, 'purchase': 'purchase_cargo_capacity'}],
'purchase_cargo_capacity' : {'type': 'cb', 'num': 0x36, 'var10': 0x0F, 'purchase': 2},
- 'articulated_part' : {'type': 'cb', 'num': 0x16, 'flag_bit': 4, 'purchase': 1}, # Don't add separate purchase CB here
+ 'articulated_part' : {'type': 'cb', 'num': 0x16, 'flag_bit': 4, 'purchase': 1}, # Don't add separate purchase CB here
'running_cost_factor' : {'type': 'cb', 'num': 0x36, 'var10': 0x09, 'purchase': 'purchase_running_cost_factor'},
'purchase_running_cost_factor' : {'type': 'cb', 'num': 0x36, 'var10': 0x09, 'purchase': 2},
'cost_factor' : {'type': 'cb', 'num': 0x36, 'var10': 0x11, 'purchase': 2},
@@ -105,7 +107,7 @@ def vehicle_length(value):
callbacks[0x02] = {
'visual_effect' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
'effect_spawn_model' : {'type': 'cb', 'num': 0x10, 'flag_bit': 0},
- 'cargo_capacity' : [ {'type': 'cb', 'num': 0x15, 'flag_bit': 3},
+ 'cargo_capacity' : [{'type': 'cb', 'num': 0x15, 'flag_bit': 3},
{'type': 'cb', 'num': 0x36, 'var10': 0x0D, 'purchase': 'purchase_cargo_capacity'}],
'purchase_cargo_capacity' : {'type': 'cb', 'num': 0x36, 'var10': 0x0D, 'purchase': 2},
'cost_factor' : {'type': 'cb', 'num': 0x36, 'var10': 0x0A, 'purchase': 2},
@@ -120,7 +122,7 @@ def vehicle_length(value):
# Aircraft
callbacks[0x03] = {
- 'passenger_capacity' : [ {'type': 'cb', 'num': 0x15, 'flag_bit': 3},
+ 'passenger_capacity' : [{'type': 'cb', 'num': 0x15, 'flag_bit': 3},
{'type': 'cb', 'num': 0x36, 'var10': 0x0F, 'purchase': 'purchase_passenger_capacity'}],
'purchase_passenger_capacity' : {'type': 'cb', 'num': 0x36, 'var10': 0x0F, 'purchase': 2},
'cost_factor' : {'type': 'cb', 'num': 0x36, 'var10': 0x0B, 'purchase': 2},
@@ -197,8 +199,8 @@ def vehicle_length(value):
'anim_control' : {'type': 'cb', 'num': 0x25},
'anim_next_frame' : {'type': 'cb', 'num': 0x26, 'flag_bit': 0},
'anim_speed' : {'type': 'cb', 'num': 0x27, 'flag_bit': 1},
- 'cargo_amount_accept' : {'type': 'cb', 'num': 0x2B, 'flag_bit': 2}, # Should work like the industry CB, i.e. call multiple times
- 'cargo_type_accept' : {'type': 'cb', 'num': 0x2C, 'flag_bit': 3}, # Should work like the industry CB, i.e. call multiple times
+ 'cargo_amount_accept' : {'type': 'cb', 'num': 0x2B, 'flag_bit': 2}, # Should work like the industry CB, i.e. call multiple times
+ 'cargo_type_accept' : {'type': 'cb', 'num': 0x2C, 'flag_bit': 3}, # Should work like the industry CB, i.e. call multiple times
'tile_check' : {'type': 'cb', 'num': 0x2F, 'flag_bit': 4},
'foundations' : {'type': 'cb', 'num': 0x30, 'flag_bit': 5},
'autoslope' : {'type': 'cb', 'num': 0x3C, 'flag_bit': 6},
@@ -210,7 +212,7 @@ def vehicle_length(value):
'construction_probability' : {'type': 'cb', 'num': 0x22, 'flag_bit': 0},
'produce_cargo_arrival' : {'type': 'cb', 'num': 0x00, 'flag_bit': 1, 'var18': 0},
'produce_256_ticks' : {'type': 'cb', 'num': 0x00, 'flag_bit': 2, 'var18': 1},
- 'location_check' : {'type': 'cb', 'num': 0x28, 'flag_bit': 3}, # We need a way to access all those special variables
+ 'location_check' : {'type': 'cb', 'num': 0x28, 'flag_bit': 3}, # We need a way to access all those special variables
'random_prod_change' : {'type': 'cb', 'num': 0x29, 'flag_bit': 4},
'monthly_prod_change' : {'type': 'cb', 'num': 0x35, 'flag_bit': 5},
'cargo_subtype_display' : {'type': 'cb', 'num': 0x37, 'flag_bit': 6},
diff --git a/nml/actions/action4.py b/nml/actions/action4.py
index 77c65e067..67078b242 100644
--- a/nml/actions/action4.py
+++ b/nml/actions/action4.py
@@ -101,11 +101,11 @@ def print_stats():
"""
Print statistics about used ids.
"""
- for t, l in string_ranges.items():
- if l["random_id"]:
- num_used = l["total"] - len(l["ids"])
+ for str_range, attrs in string_ranges.items():
+ if attrs["random_id"]:
+ num_used = attrs["total"] - len(attrs["ids"])
if num_used > 0:
- generic.print_info("{:02X}xx strings: {}/{}".format(t, num_used, l["total"]))
+ generic.print_info("{:02X}xx strings: {}/{}".format(str_range, num_used, attrs["total"]))
def get_global_string_actions():
@@ -179,16 +179,15 @@ def get_string_action4s(feature, string_range, string, id=None):
# ID is allocated randomly, we will output the actions later
write_action4s = False
if (feature, string) in used_strings[string_range]:
- id_val = used_strings[string_range][(feature, string)]
+ id_val = used_strings[string_range][feature, string]
else:
try:
id_val = string_ranges[string_range]["ids"].pop()
- used_strings[string_range][(feature, string)] = id_val
+ used_strings[string_range][feature, string] = id_val
except IndexError:
raise generic.ScriptError(
- "Unable to allocate ID for string, no more free IDs available (maximum is {:d})".format(
- string_ranges[string_range]["total"]
- ),
+ "Unable to allocate ID for string, no more free IDs available"
+ f" (maximum is {string_ranges[string_range]['total']})",
string.pos,
)
else:
diff --git a/nml/actions/action5.py b/nml/actions/action5.py
index 4c16955d1..f2f8288ac 100644
--- a/nml/actions/action5.py
+++ b/nml/actions/action5.py
@@ -100,30 +100,34 @@ def parse_action5(replaces):
if block_type == Action5BlockType.FIXED:
if num_sprites < num_required:
- msg = "Invalid sprite count for sprite replacement type '{}', expected {:d}, got {:d}"
- msg = msg.format(replaces.type, num_required, num_sprites)
+ msg = (
+ f"Invalid sprite count for sprite replacement type '{replaces.type}',"
+ f" expected {num_required}, got {num_sprites}"
+ )
raise generic.ScriptError(msg, replaces.pos)
elif num_sprites > num_required:
msg = (
- "Too many sprites specified for sprite replacement type '{}',"
- " expected {:d}, got {:d}, extra sprites may be ignored"
- ).format(replaces.type, num_required, num_sprites)
+ f"Too many sprites specified for sprite replacement type '{replaces.type}',"
+ f" expected {num_required}, got {num_sprites}, extra sprites may be ignored"
+ )
generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)
if replaces.offset != 0:
- msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
+ msg = f"replacenew parameter 'offset' must be zero for sprite replacement type '{replaces.type}'"
raise generic.ScriptError(msg, replaces.pos)
elif block_type == Action5BlockType.ANY:
if replaces.offset != 0:
- msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
+ msg = f"replacenew parameter 'offset' must be zero for sprite replacement type '{replaces.type}'"
raise generic.ScriptError(msg, replaces.pos)
elif block_type == Action5BlockType.OFFSET:
if num_sprites + replaces.offset > num_required:
- msg = "Exceeding the limit of {:d} sprites for sprite replacement type '{}', extra sprites may be ignored"
- msg = msg.format(num_required, replaces.type)
+ msg = (
+ f"Exceeding the limit of {num_required} sprites for sprite replacement type '{replaces.type}',"
+ " extra sprites may be ignored"
+ )
generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)
if replaces.offset != 0 or num_sprites != num_required:
diff --git a/nml/actions/action6.py b/nml/actions/action6.py
index 0f23a0750..2729b4f32 100644
--- a/nml/actions/action6.py
+++ b/nml/actions/action6.py
@@ -28,11 +28,9 @@ def print_stats():
Print statistics about used ids.
"""
if free_parameters.stats[0] > 0:
- generic.print_info(
- "Concurrent ActionD registers: {}/{} ({})".format(
- free_parameters.stats[0], free_parameters.total_amount, str(free_parameters.stats[1])
- )
- )
+ used = free_parameters.stats[0]
+ total = free_parameters.total_amount
+ generic.print_info(f"Concurrent ActionD registers: {used}/{total} ({free_parameters.stats[1]})")
class Action6(base_action.BaseAction):
diff --git a/nml/actions/action7.py b/nml/actions/action7.py
index 44dbf9a28..5c38f937f 100644
--- a/nml/actions/action7.py
+++ b/nml/actions/action7.py
@@ -29,9 +29,7 @@ def print_stats():
"""
if free_labels.stats[0] > 0:
generic.print_info(
- "Concurrent Action10 labels: {}/{} ({})".format(
- free_labels.stats[0], free_labels.total_amount, str(free_labels.stats[1])
- )
+ f"Concurrent Action10 labels: {free_labels.stats[0]}/{free_labels.total_amount} ({free_labels.stats[1]})"
)
@@ -88,7 +86,7 @@ def op_to_cond_op(op):
if op == nmlop.CMP_LE:
return (5, r"\7>")
# Not reached
- raise ValueError("Unexpected operator '{}'".format(op.token))
+ raise ValueError(f"Unexpected operator '{op.token}'")
def parse_conditional(expr):
diff --git a/nml/actions/actionD.py b/nml/actions/actionD.py
index 8a8a889f4..cbcdb0cc5 100644
--- a/nml/actions/actionD.py
+++ b/nml/actions/actionD.py
@@ -54,10 +54,10 @@ def write(self, file):
size += 4
# print the statement for easier debugging
- str1 = "param[{}]".format(self.param1) if self.param1.value != 0xFF else str(self.data)
- str2 = "param[{}]".format(self.param2) if self.param2.value != 0xFF else str(self.data)
+ str1 = f"param[{self.param1}]" if self.param1.value != 0xFF else str(self.data)
+ str2 = f"param[{self.param2}]" if self.param2.value != 0xFF else str(self.data)
str_total = self.op.to_string(str1, str2) if self.op != nmlop.ASSIGN else str1
- file.comment("param[{}] = {}".format(self.target, str_total))
+ file.comment(f"param[{self.target}] = {str_total}")
file.start_sprite(size)
file.print_bytex(0x0D)
@@ -97,7 +97,7 @@ def register_names(self):
if global_constants.identifier_refcount[self.param.value] == 0:
generic.print_warning(
generic.Warning.OPTIMISATION,
- "Named parameter '{}' is not referenced, ignoring.".format(self.param.value),
+ f"Named parameter '{self.param.value}' is not referenced, ignoring.",
self.param.pos,
)
return
@@ -110,7 +110,7 @@ def pre_process(self):
if isinstance(self.param, expression.SpecialParameter):
if not self.param.can_assign():
raise generic.ScriptError(
- "Trying to assign a value to the read-only variable '{}'".format(self.param.name), self.param.pos
+ f"Trying to assign a value to the read-only variable '{self.param.name}'", self.param.pos
)
elif isinstance(self.param, expression.Identifier):
return
@@ -126,7 +126,7 @@ def get_action_list(self):
return parse_actionD(self)
def __str__(self):
- return "{} = {};\n".format(self.param, self.value)
+ return f"{self.param} = {self.value};\n"
# prevent evaluating common sub-expressions multiple times
diff --git a/nml/actions/actionF.py b/nml/actions/actionF.py
index 790a92b4c..7800f676f 100644
--- a/nml/actions/actionF.py
+++ b/nml/actions/actionF.py
@@ -1,3 +1,7 @@
+"""
+Code for storing and generating action F
+"""
+
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,9 +17,6 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-"""
-Code for storing and generating action F
-"""
from nml import expression, generic, grfstrings
from nml.actions import base_action
@@ -64,7 +65,7 @@ def print_stats():
"""
num_used = total_numbers - len(free_numbers)
if num_used > 0:
- generic.print_info("Town names: {}/{}".format(num_used, total_numbers))
+ generic.print_info(f"Town names: {num_used}/{total_numbers}")
class ActionF(base_action.BaseAction):
@@ -115,9 +116,7 @@ def prepare_output(self, sprite_num):
self.id_number = get_free_id()
if isinstance(self.name, expression.Identifier):
if self.name.value in named_numbers:
- raise generic.ScriptError(
- 'Cannot define town name "{}", it is already in use'.format(self.name), self.pos
- )
+ raise generic.ScriptError(f'Cannot define town name "{self.name}", it is already in use', self.pos)
named_numbers[self.name.value] = self.id_number # Add name to the set 'safe' names.
else:
numbered_numbers.add(self.id_number) # Add number to the set of 'safe' numbers.
@@ -157,7 +156,7 @@ def update_block(block, startbit):
if startbit > 32:
raise generic.ScriptError(
- "Not enough random bits for the town name generation ({:d} needed, 32 available)".format(startbit),
+ f"Not enough random bits for the town name generation ({startbit} needed, 32 available)",
self.pos,
)
@@ -172,7 +171,7 @@ def update_block(block, startbit):
self.style_names.sort()
if len(self.style_names) == 0:
raise generic.ScriptError(
- 'Style "{}" defined, but no translations found for it'.format(self.style_name.name.value), self.pos
+ f'Style "{self.style_name.name.value}" defined, but no translations found for it', self.pos
)
else:
self.style_names = []
diff --git a/nml/actions/base_action.py b/nml/actions/base_action.py
index 774e75548..582b7d604 100644
--- a/nml/actions/base_action.py
+++ b/nml/actions/base_action.py
@@ -33,7 +33,7 @@ def write(self, file):
@param file: The outputfile to write the data to.
@type file: L{SpriteOutputBase}
"""
- raise NotImplementedError("write is not implemented in {!r}".format(type(self)))
+ raise NotImplementedError(f"write is not implemented in {type(self)!r}")
def skip_action7(self):
"""
diff --git a/nml/actions/real_sprite.py b/nml/actions/real_sprite.py
index 5cb016965..bc212a89a 100644
--- a/nml/actions/real_sprite.py
+++ b/nml/actions/real_sprite.py
@@ -13,12 +13,6 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-try:
- from PIL import Image
-except ImportError:
- # Image is required only when using graphics
- pass
-
from nml import expression, generic
from nml.actions import base_action
from nml.ast import assignment
@@ -211,6 +205,9 @@ def validate_size(self):
Check if xpos/ypos/xsize/ysize are already set and if not, set them
to 0,0,image_width,image_height.
"""
+ # Image is required only when using graphics, existence already checked by entrypoint
+ from PIL import Image
+
if self.xpos is None:
with Image.open(generic.find_file(self.file.value)) as im:
self.xpos = expression.ConstantNumeric(0)
@@ -283,8 +280,10 @@ def __init__(self):
def prepare_output(self, sprite_num):
if self.sprite_num is not None and self.sprite_num.value != sprite_num:
- msg = "Sprite number {:d} given in base_graphics-block, but it doesn't match output sprite number {:d}"
- msg = msg.format(self.sprite_num.value, sprite_num)
+ msg = (
+ f"Sprite number {self.sprite_num.value} given in base_graphics-block,"
+ f" but it doesn't match output sprite number {sprite_num}"
+ )
raise generic.ScriptError(msg)
@@ -317,7 +316,7 @@ def __init__(self, mapping, label=None, poslist=None):
def debug_print(self, indentation):
generic.print_dbg(indentation, "Recolour sprite, mapping:")
for recolour in self.mapping:
- generic.print_dbg(indentation + 2, "{}: {};".format(recolour.name, recolour.value))
+ generic.print_dbg(indentation + 2, f"{recolour.name}: {recolour.value};")
def get_labels(self):
labels = {}
@@ -352,7 +351,7 @@ def __str__(self):
ret = "" if self.label is None else str(self.label) + ": "
ret += "recolour_sprite {\n"
for recolour in self.mapping:
- ret += "{}: {};".format(recolour.name, recolour.value)
+ ret += f"{recolour.name}: {recolour.value};"
ret += "}"
return ret
@@ -382,10 +381,7 @@ def prepare_output(self, sprite_num):
val += i
colour_mapping[idx] = val
for i in range(256):
- if i in colour_mapping:
- colour = colour_mapping[i]
- else:
- colour = i
+ colour = colour_mapping.get(i, i)
self.output_table.append(colour)
def write(self, file):
@@ -428,7 +424,7 @@ def get_labels(self):
if self.label is not None:
if self.label.value in labels:
raise generic.ScriptError(
- "Duplicate label encountered; '{}' already exists.".format(self.label.value), self.pos
+ f"Duplicate label encountered; '{self.label.value}' already exists.", self.pos
)
labels[self.label.value] = 0
return labels, offset
@@ -492,14 +488,14 @@ def parse_real_sprite(sprite, default_file, default_mask_file, poslist, id_dict)
new_sprite.xrel.value,
-0x8000,
0x7FFF,
- "Real sprite parameter {:d} 'xrel'".format(param_offset + 1),
+ f"Real sprite parameter {param_offset + 1} 'xrel'",
new_sprite.xrel.pos,
)
generic.check_range(
new_sprite.yrel.value,
-0x8000,
0x7FFF,
- "Real sprite parameter {:d} 'yrel'".format(param_offset + 2),
+ f"Real sprite parameter {param_offset + 2} 'yrel'",
new_sprite.yrel.pos,
)
param_offset += 2
@@ -520,7 +516,7 @@ def parse_real_sprite(sprite, default_file, default_mask_file, poslist, id_dict)
param_offset += 1
if not isinstance(new_sprite.file, expression.StringLiteral):
raise generic.ScriptError(
- "Real sprite parameter {:d} 'file' should be a string literal".format(param_offset + 1),
+ f"Real sprite parameter {param_offset + 1} 'file' should be a string literal",
new_sprite.file.pos,
)
@@ -537,7 +533,7 @@ def parse_real_sprite(sprite, default_file, default_mask_file, poslist, id_dict)
if isinstance(mask, expression.Array):
if not (0 <= len(mask.values) <= 3):
raise generic.ScriptError(
- "Real sprite mask should be an array with 0 to 3 values, encountered {:d}".format(len(mask.values)),
+ f"Real sprite mask should be an array with 0 to 3 values, encountered {len(mask.values)}",
mask.pos,
)
if len(mask.values) == 0:
@@ -561,15 +557,13 @@ def parse_real_sprite(sprite, default_file, default_mask_file, poslist, id_dict)
new_sprite.mask_file = mask.reduce([id_dict])
if not isinstance(new_sprite.mask_file, expression.StringLiteral):
raise generic.ScriptError(
- "Real sprite parameter {:d} 'mask' should be an array or string literal".format(param_offset + 1),
+ f"Real sprite parameter {param_offset + 1} 'mask' should be an array or string literal",
new_sprite.file.pos,
)
if num_param > param_offset:
raise generic.ScriptError(
- "Real sprite has too many parameters, the last {:d} parameter(s) cannot be parsed.".format(
- num_param - param_offset
- ),
+ f"Real sprite has too many parameters, the last {num_param - param_offset} parameter(s) cannot be parsed.",
sprite.param_list[param_offset].pos,
)
@@ -602,9 +596,11 @@ def parse_sprite_data(sprite_container):
sprite_list, default_file, default_mask_file, pos, zoom_level, bit_depth = sprite_data
new_sprite_list = parse_sprite_list(sprite_list, default_file, default_mask_file, [pos])
if not first and len(new_sprite_list) != len(action_list):
- msg = "Expected {:d} alternative sprites for {} '{}', got {:d}."
- msg = msg.format(
- len(action_list), sprite_container.block_type, sprite_container.block_name.value, len(new_sprite_list)
+ sprite_type = sprite_container.block_type
+ sprite_name = sprite_container.block_name.value
+ msg = (
+ f"Expected {len(action_list)} alternative sprites for {sprite_type} '{sprite_name}',"
+ f"got {len(new_sprite_list)}."
)
raise generic.ScriptError(msg, sprite_container.pos)
diff --git a/nml/ast/alt_sprites.py b/nml/ast/alt_sprites.py
index a07d17c17..f363e96f6 100644
--- a/nml/ast/alt_sprites.py
+++ b/nml/ast/alt_sprites.py
@@ -131,6 +131,6 @@ def __str__(self):
params.append(self.mask_file)
ret = "alternative_sprites({}) {{\n".format(", ".join(str(p) for p in params))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
diff --git a/nml/ast/assignment.py b/nml/ast/assignment.py
index 99e631099..db9817350 100644
--- a/nml/ast/assignment.py
+++ b/nml/ast/assignment.py
@@ -46,7 +46,7 @@ def debug_print(self, indentation):
self.value.debug_print(indentation + 4)
def __str__(self):
- return "{}: {};".format(self.name, self.value)
+ return f"{self.name}: {self.value};"
class UnitAssignment(Assignment):
@@ -73,7 +73,7 @@ def __str__(self):
if self.unit is None:
return Assignment.__str__(self)
else:
- return "{}: {} {};".format(self.name, self.value, self.unit.name)
+ return f"{self.name}: {self.value} {self.unit.name};"
class Range:
@@ -96,4 +96,4 @@ def __init__(self, min, max):
def __str__(self):
if self.max is None:
return str(self.min)
- return "{} .. {}".format(self.min, self.max)
+ return f"{self.min} .. {self.max}"
diff --git a/nml/ast/base_graphics.py b/nml/ast/base_graphics.py
index bb8f5771c..a004a603e 100644
--- a/nml/ast/base_graphics.py
+++ b/nml/ast/base_graphics.py
@@ -39,9 +39,7 @@ def __init__(self, param_list, sprite_list, name, pos):
num_params = len(param_list)
if not (0 <= num_params <= 2):
- raise generic.ScriptError(
- "base_graphics-block requires 0 to 2 parameters, encountered {:d}".format(num_params), pos
- )
+ raise generic.ScriptError(f"base_graphics-block requires 0 to 2 parameters, encountered {num_params}", pos)
if num_params >= 2:
self.sprite_num = param_list[0].reduce_constant()
else:
@@ -82,6 +80,6 @@ def __str__(self):
params.append(self.image_file)
ret = "base_graphics {}({}) {{\n".format(name, ", ".join(str(param) for param in params))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
diff --git a/nml/ast/base_statement.py b/nml/ast/base_statement.py
index 01a9f5c4c..5129451d6 100644
--- a/nml/ast/base_statement.py
+++ b/nml/ast/base_statement.py
@@ -60,20 +60,18 @@ def validate(self, scope_list):
"""
seen_item = False
for scope in scope_list:
- if scope.list_type == BaseStatementList.LIST_TYPE_SKIP:
+ if scope.list_type == BaseStatementList.LIST_TYPE_SKIP: # noqa: SIM102 # match block
if not self.bs_skipable:
- raise generic.ScriptError(
- "{} may not appear inside a conditional block.".format(self.bs_name), self.pos
- )
- if scope.list_type == BaseStatementList.LIST_TYPE_LOOP:
+ raise generic.ScriptError(f"{self.bs_name} may not appear inside a conditional block.", self.pos)
+ if scope.list_type == BaseStatementList.LIST_TYPE_LOOP: # noqa: SIM102 # match block
if not self.bs_loopable:
- raise generic.ScriptError("{} may not appear inside a loop.".format(self.bs_name), self.pos)
+ raise generic.ScriptError(f"{self.bs_name} may not appear inside a loop.", self.pos)
if scope.list_type == BaseStatementList.LIST_TYPE_ITEM:
seen_item = True
if not self.bs_in_item:
- raise generic.ScriptError("{} may not appear inside an item block.".format(self.bs_name), self.pos)
+ raise generic.ScriptError(f"{self.bs_name} may not appear inside an item block.", self.pos)
if not (seen_item or self.bs_out_item):
- raise generic.ScriptError("{} must appear inside an item block.".format(self.bs_name), self.pos)
+ raise generic.ScriptError(f"{self.bs_name} must appear inside an item block.", self.pos)
def register_names(self):
"""
@@ -95,7 +93,7 @@ def debug_print(self, indentation):
@param indentation: Print all lines with at least C{indentation} spaces
@type indentation: C{int}
"""
- raise NotImplementedError("debug_print must be implemented in BaseStatement-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"debug_print must be implemented in BaseStatement-subclass {type(self)!r}")
def get_action_list(self):
"""
@@ -104,9 +102,7 @@ def get_action_list(self):
@return: A list of action
@rtype: C{list} of L{BaseAction}
"""
- raise NotImplementedError(
- "get_action_list must be implemented in BaseStatement-subclass {!r}".format(type(self))
- )
+ raise NotImplementedError(f"get_action_list must be implemented in BaseStatement-subclass {type(self)!r}")
def __str__(self):
"""
@@ -115,7 +111,7 @@ def __str__(self):
@return: An NML string representing this action
@rtype: C{str}
"""
- raise NotImplementedError("__str__ must be implemented in BaseStatement-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"__str__ must be implemented in BaseStatement-subclass {type(self)!r}")
class BaseStatementList(BaseStatement):
diff --git a/nml/ast/basecost.py b/nml/ast/basecost.py
index 286938af6..c001a2e72 100644
--- a/nml/ast/basecost.py
+++ b/nml/ast/basecost.py
@@ -55,7 +55,7 @@ def pre_process(self):
new_costs.extend(tmp_list)
else:
raise generic.ScriptError(
- "Unrecognized base cost identifier '{}' encountered".format(cost.name.value), cost.name.pos
+ f"Unrecognized base cost identifier '{cost.name.value}' encountered", cost.name.pos
)
else:
cost.name = cost.name.reduce()
@@ -75,7 +75,7 @@ def get_action_list(self):
def __str__(self):
ret = "basecost {\n"
for cost in self.costs:
- ret += "\t{}: {};\n".format(cost.name, cost.value)
+ ret += f"\t{cost.name}: {cost.value};\n"
ret += "}\n"
return ret
diff --git a/nml/ast/cargotable.py b/nml/ast/cargotable.py
index c18dd43bf..665f832d2 100644
--- a/nml/ast/cargotable.py
+++ b/nml/ast/cargotable.py
@@ -34,7 +34,7 @@ def register_names(self):
if self.cargo_list[i].value in global_constants.cargo_numbers:
generic.print_warning(
generic.Warning.GENERIC,
- "Duplicate entry in cargo table: {}".format(self.cargo_list[i].value),
+ f"Duplicate entry in cargo table: {self.cargo_list[i].value}",
cargo.pos,
)
else:
diff --git a/nml/ast/conditional.py b/nml/ast/conditional.py
index c4465bd60..d5e56687c 100644
--- a/nml/ast/conditional.py
+++ b/nml/ast/conditional.py
@@ -79,7 +79,7 @@ def debug_print(self, indentation):
def __str__(self):
ret = ""
if self.expr is not None:
- ret += "if ({})".format(self.expr)
+ ret += f"if ({self.expr})"
ret += " {\n"
ret += base_statement.BaseStatementList.__str__(self)
ret += "}\n"
diff --git a/nml/ast/constant.py b/nml/ast/constant.py
index d7e15dbf1..59bcbc3b7 100644
--- a/nml/ast/constant.py
+++ b/nml/ast/constant.py
@@ -27,7 +27,7 @@ def register_names(self):
if not isinstance(self.name, expression.Identifier):
raise generic.ScriptError("Constant name should be an identifier", self.name.pos)
if self.name.value in global_constants.constant_numbers:
- raise generic.ScriptError("Redefinition of constant '{}'.".format(self.name.value), self.name.pos)
+ raise generic.ScriptError(f"Redefinition of constant '{self.name.value}'.", self.name.pos)
global_constants.constant_numbers[self.name.value] = self.value.reduce_constant(
global_constants.const_list
).value
@@ -44,4 +44,4 @@ def get_action_list(self):
return []
def __str__(self):
- return "const {} = {};".format(self.name, self.value)
+ return f"const {self.name} = {self.value};"
diff --git a/nml/ast/disable_item.py b/nml/ast/disable_item.py
index f2edf10c5..fa544b483 100644
--- a/nml/ast/disable_item.py
+++ b/nml/ast/disable_item.py
@@ -36,7 +36,7 @@ def __init__(self, param_list, pos):
base_statement.BaseStatement.__init__(self, "disable_item()", pos)
if not (1 <= len(param_list) <= 3):
raise generic.ScriptError(
- "disable_item() requires between 1 and 3 parameters, encountered {:d}.".format(len(param_list)), pos
+ f"disable_item() requires between 1 and 3 parameters, encountered {len(param_list)}.", pos
)
self.feature = general.parse_feature(param_list[0])
self.first_id = param_list[1] if len(param_list) > 1 else None
@@ -66,7 +66,7 @@ def __str__(self):
ret += ", " + str(self.first_id)
if self.last_id is not None:
ret += ", " + str(self.last_id)
- return "disable_item({});\n".format(ret)
+ return f"disable_item({ret});\n"
def get_action_list(self):
return action0.get_disable_actions(self)
diff --git a/nml/ast/error.py b/nml/ast/error.py
index 42cb86880..f28a39599 100644
--- a/nml/ast/error.py
+++ b/nml/ast/error.py
@@ -89,12 +89,12 @@ def __str__(self):
if self.severity.value == actionB.error_severity[s]:
sev = s
break
- res = "error({}, {}".format(sev, self.msg)
+ res = f"error({sev}, {self.msg}"
if self.data is not None:
- res += ", {}".format(self.data)
+ res += f", {self.data}"
if len(self.params) > 0:
- res += ", {}".format(self.params[0])
+ res += f", {self.params[0]}"
if len(self.params) > 1:
- res += ", {}".format(self.params[1])
+ res += f", {self.params[1]}"
res += ");\n"
return res
diff --git a/nml/ast/font.py b/nml/ast/font.py
index 12a3b642c..56ad57dcf 100644
--- a/nml/ast/font.py
+++ b/nml/ast/font.py
@@ -74,6 +74,6 @@ def __str__(self):
params.append(self.image_file)
ret = "font_glyph {}({}) {{\n".format(name, ", ".join(str(param) for param in params))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
diff --git a/nml/ast/general.py b/nml/ast/general.py
index 93656c637..6ac74ea9a 100644
--- a/nml/ast/general.py
+++ b/nml/ast/general.py
@@ -55,7 +55,7 @@ def feature_name(feature):
for name, num in feature_ids.items():
if num == feature:
return name
- raise AssertionError("Invalid feature number '{}'.".format(feature))
+ raise AssertionError(f"Invalid feature number '{feature}'.")
def parse_feature(expr):
@@ -70,7 +70,7 @@ def parse_feature(expr):
"""
expr = expr.reduce_constant([feature_ids])
if expr.value not in feature_ids.values():
- raise generic.ScriptError("Invalid feature '{:02X}' encountered.".format(expr.value), expr.pos)
+ raise generic.ScriptError(f"Invalid feature '{expr.value:02X}' encountered.", expr.pos)
return expr
diff --git a/nml/ast/grf.py b/nml/ast/grf.py
index 3440b1fd4..91e7076ad 100644
--- a/nml/ast/grf.py
+++ b/nml/ast/grf.py
@@ -33,7 +33,7 @@ def print_stats():
Print statistics about used ids.
"""
if param_stats[0] > 0:
- generic.print_info("GRF parameter registers: {}/{}".format(param_stats[0], param_stats[1]))
+ generic.print_info(f"GRF parameter registers: {param_stats[0]}/{param_stats[1]}")
def set_palette_used(pal):
@@ -193,13 +193,13 @@ def get_action_list(self):
def __str__(self):
ret = "grf {\n"
- ret += "\tgrfid: {};\n".format(str(self.grfid))
- ret += "\tname: {};\n".format(str(self.name))
- ret += "\tdesc: {};\n".format(str(self.desc))
+ ret += f"\tgrfid: {self.grfid};\n"
+ ret += f"\tname: {self.name};\n"
+ ret += f"\tdesc: {self.desc};\n"
if self.url is not None:
- ret += "\turl: {};\n".format(self.url)
- ret += "\tversion: {};\n".format(self.version)
- ret += "\tmin_compatible_version: {};\n".format(self.min_compatible_version)
+ ret += f"\turl: {self.url};\n"
+ ret += f"\tversion: {self.version};\n"
+ ret += f"\tmin_compatible_version: {self.min_compatible_version};\n"
for param in self.params:
ret += str(param)
ret += "}\n"
@@ -225,15 +225,15 @@ def pre_process(self):
self.set_property(set_val.name.value, set_val.value)
def __str__(self):
- ret = "\t\t{} {{\n".format(self.name)
+ ret = f"\t\t{self.name} {{\n"
for val in self.value_list:
if val.name.value == "names":
ret += "\t\t\tnames: {\n"
for name in val.value.values:
- ret += "\t\t\t\t{}: {};\n".format(name.name, name.value)
+ ret += f"\t\t\t\t{name.name}: {name.value};\n"
ret += "\t\t\t};\n"
else:
- ret += "\t\t\t{}: {};\n".format(val.name, val.value)
+ ret += f"\t\t\t{val.name}: {val.value};\n"
ret += "\t\t}\n"
return ret
@@ -352,9 +352,7 @@ def pre_process(self, num):
setting.bit_num = expression.ConstantNumeric(ParameterDescription.free_bits[self.num.value].pop(0))
else:
if setting.bit_num.value not in ParameterDescription.free_bits[self.num.value]:
- raise generic.ScriptError(
- "Bit {} is already used".format(setting.bit_num.value), setting.name.pos
- )
+ raise generic.ScriptError(f"Bit {setting.bit_num.value} is already used", setting.name.pos)
ParameterDescription.free_bits[self.num.value].remove(setting.bit_num.value)
global_constants.misc_grf_bits[setting.name.value] = {
"param": self.num.value,
diff --git a/nml/ast/item.py b/nml/ast/item.py
index 87cc32901..042e308e8 100644
--- a/nml/ast/item.py
+++ b/nml/ast/item.py
@@ -44,9 +44,7 @@ def __init__(self, params, body, pos):
)
if not (1 <= len(params) <= 4):
- raise generic.ScriptError(
- "Item block requires between 1 and 4 parameters, found {:d}.".format(len(params)), self.pos
- )
+ raise generic.ScriptError(f"Item block requires between 1 and 4 parameters, found {len(params)}.", self.pos)
self.feature = general.parse_feature(params[0])
if self.feature.value in (0x08, 0x0C, 0x0E):
raise generic.ScriptError("Defining item blocks for this feature is not allowed.", self.pos)
@@ -76,9 +74,10 @@ def register_names(self):
if self.id is not None and existing_id.value != self.id.value:
raise generic.ScriptError(
(
- "Duplicate item with name '{}'."
- " This item has already been assigned to id {:d}, cannot reassign to id {:d}"
- ).format(self.name.value, existing_id.value, self.id.value),
+ f"Duplicate item with name '{self.name.value}'."
+ f" This item has already been assigned to id {existing_id.value},"
+ f" cannot reassign to id {self.id.value}"
+ ),
self.pos,
)
self.id = existing_id
@@ -112,13 +111,13 @@ def get_action_list(self):
return base_statement.BaseStatementList.get_action_list(self)
def __str__(self):
- ret = "item({:d}".format(self.feature.value)
+ ret = f"item({self.feature.value}"
if self.name is not None:
- ret += ", {}".format(self.name)
+ ret += f", {self.name}"
ret += ", {}".format(str(self.id) if self.id is not None else "-1")
if self.size is not None:
sizes = ["1X1", None, "2X1", "1X2", "2X2"]
- ret += ", HOUSE_SIZE_{}".format(sizes[self.size.value])
+ ret += f", HOUSE_SIZE_{sizes[self.size.value]}"
ret += ") {\n"
ret += base_statement.BaseStatementList.__str__(self)
ret += "}\n"
@@ -158,7 +157,7 @@ def debug_print(self, indentation):
def __str__(self):
unit = "" if self.unit is None else " " + str(self.unit)
- return "\t{}: {}{};".format(self.name, self.value, unit)
+ return f"\t{self.name}: {self.value}{unit};"
class PropertyBlock(base_statement.BaseStatement):
@@ -189,7 +188,7 @@ def get_action_list(self):
def __str__(self):
ret = "property {\n"
for prop in self.prop_list:
- ret += "{}\n".format(prop)
+ ret += f"{prop}\n"
ret += "}\n"
return ret
@@ -215,11 +214,11 @@ def get_action_list(self):
return action3.parse_graphics_block(self.graphics_block, item_feature, wagon_id, item_size, True)
def __str__(self):
- ret = "livery_override({}) {{\n".format(self.wagon_id)
+ ret = f"livery_override({self.wagon_id}) {{\n"
for graphics in self.graphics_block.graphics_list:
- ret += "\t{}\n".format(graphics)
+ ret += f"\t{graphics}\n"
if self.graphics_block.default_graphics is not None:
- ret += "\t{}\n".format(self.graphics_block.default_graphics)
+ ret += f"\t{self.graphics_block.default_graphics}\n"
ret += "}\n"
return ret
@@ -274,9 +273,9 @@ def get_action_list(self):
def __str__(self):
ret = "graphics {\n"
for graphics in self.graphics_list:
- ret += "\t{}\n".format(graphics)
+ ret += f"\t{graphics}\n"
if self.default_graphics is not None:
- ret += "\t{}\n".format(self.default_graphics)
+ ret += f"\t{self.default_graphics}\n"
ret += "}\n"
return ret
@@ -303,4 +302,4 @@ def debug_print(self, indentation):
self.result.debug_print(indentation + 2)
def __str__(self):
- return "{}: {}".format(self.cargo_id, self.result)
+ return f"{self.cargo_id}: {self.result}"
diff --git a/nml/ast/loop.py b/nml/ast/loop.py
index cf2e861db..6a55d668a 100644
--- a/nml/ast/loop.py
+++ b/nml/ast/loop.py
@@ -48,7 +48,7 @@ def get_action_list(self):
return action7.parse_loop_block(self)
def __str__(self):
- ret = "while({}) {{\n".format(self.expr)
+ ret = f"while({self.expr}) {{\n"
ret += base_statement.BaseStatementList.__str__(self)
ret += "}\n"
return ret
diff --git a/nml/ast/produce.py b/nml/ast/produce.py
index 880927fa6..9f89d5f3e 100644
--- a/nml/ast/produce.py
+++ b/nml/ast/produce.py
@@ -35,7 +35,7 @@ def __init__(self, param_list, pos):
base_statement.BaseStatement.__init__(self, "produce-block", pos, False, False)
if not (6 <= len(param_list) <= 7):
raise generic.ScriptError(
- "produce-block requires 6 or 7 parameters, encountered {:d}".format(len(param_list)), self.pos
+ f"produce-block requires 6 or 7 parameters, encountered {len(param_list)}", self.pos
)
name = param_list[0]
if not isinstance(name, expression.Identifier):
@@ -48,7 +48,7 @@ def __init__(self, param_list, pos):
def pre_process(self):
generic.print_warning(
generic.Warning.DEPRECATION,
- "Consider using the new produce() syntax for '{}'".format(self.name),
+ f"Consider using the new produce() syntax for '{self.name}'",
self.name.pos,
)
var_scope = action2var.get_scope(0x0A)
@@ -128,7 +128,7 @@ def collect_references(self):
return all_refs
def __str__(self):
- return "produce({0}, [{1}], [{2}], {3})\n".format(
+ return "produce({}, [{}], [{}], {})\n".format(
str(self.name),
" ".join(str(x) for x in self.subtract_in),
" ".join(str(x) for x in self.add_out),
@@ -138,10 +138,10 @@ def __str__(self):
def debug_print(self, indentation):
generic.print_dbg(indentation, "Produce, name =", self.name)
for cargopair in self.subtract_in:
- generic.print_dbg(indentation + 2, "Subtract from input {0}:".format(cargopair.name.value))
+ generic.print_dbg(indentation + 2, f"Subtract from input {cargopair.name.value}:")
cargopair.value.debug_print(indentation + 4)
for cargopair in self.add_out:
- generic.print_dbg(indentation + 2, "Add to output {0}:".format(cargopair.name.value))
+ generic.print_dbg(indentation + 2, f"Add to output {cargopair.name.value}:")
cargopair.value.debug_print(indentation + 4)
generic.print_dbg(indentation + 2, "Again:")
self.again.debug_print(indentation + 4)
diff --git a/nml/ast/replace.py b/nml/ast/replace.py
index 71c723f4b..ac9f5dcc3 100644
--- a/nml/ast/replace.py
+++ b/nml/ast/replace.py
@@ -81,9 +81,9 @@ def get_action_list(self):
def __str__(self):
name = str(self.block_name) if self.block_name is not None else ""
def_file = "" if self.image_file is None else ", " + str(self.image_file)
- ret = "replace {}({}{}) {{\n".format(name, self.start_id, def_file)
+ ret = f"replace {name}({self.start_id}{def_file}) {{\n"
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
@@ -164,6 +164,6 @@ def __str__(self):
params.append(self.offset)
ret = "replacenew {}({}) {{\n".format(name, ", ".join(str(param) for param in params))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
diff --git a/nml/ast/snowline.py b/nml/ast/snowline.py
index 95e771fee..43778ef21 100644
--- a/nml/ast/snowline.py
+++ b/nml/ast/snowline.py
@@ -41,7 +41,7 @@ def __init__(self, line_type, height_data, pos):
self.date_heights = height_data
def debug_print(self, indentation):
- generic.print_dbg(indentation, "Snowline (type={})".format(self.type))
+ generic.print_dbg(indentation, f"Snowline (type={self.type})")
for dh in self.date_heights:
dh.debug_print(indentation + 2)
diff --git a/nml/ast/sort_vehicles.py b/nml/ast/sort_vehicles.py
index 35d6c744b..3d3ed23aa 100644
--- a/nml/ast/sort_vehicles.py
+++ b/nml/ast/sort_vehicles.py
@@ -32,9 +32,7 @@ class SortVehicles(base_statement.BaseStatement):
def __init__(self, params, pos):
base_statement.BaseStatement.__init__(self, "sort-block", pos)
if len(params) != 2:
- raise generic.ScriptError(
- "Sort-block requires exactly two parameters, got {:d}".format(len(params)), self.pos
- )
+ raise generic.ScriptError(f"Sort-block requires exactly two parameters, got {len(params)}", self.pos)
self.feature = general.parse_feature(params[0])
self.vehid_list = params[1]
@@ -57,4 +55,4 @@ def get_action_list(self):
return action0.parse_sort_block(self.feature.value, self.vehid_list.values)
def __str__(self):
- return "sort({:d}, {});\n".format(self.feature.value, self.vehid_list)
+ return f"sort({self.feature.value}, {self.vehid_list});\n"
diff --git a/nml/ast/sprite_container.py b/nml/ast/sprite_container.py
index 0f91dc1a1..9b1c85af6 100644
--- a/nml/ast/sprite_container.py
+++ b/nml/ast/sprite_container.py
@@ -43,9 +43,7 @@ def __init__(self, block_type, block_name):
self.sprite_data = {}
if block_name is not None:
if block_name.value in SpriteContainer.sprite_blocks:
- raise generic.ScriptError(
- "Block with name '{}' is already defined.".format(block_name.value), block_name.pos
- )
+ raise generic.ScriptError(f"Block with name '{block_name.value}' is already defined.", block_name.pos)
SpriteContainer.sprite_blocks[block_name.value] = self
def add_sprite_data(self, sprite_list, default_file, pos, zoom_level=0, bit_depth=8, default_mask_file=None):
@@ -80,6 +78,4 @@ def get_all_sprite_data(self):
def resolve_sprite_block(cls, block_name):
if block_name.value in cls.sprite_blocks:
return cls.sprite_blocks[block_name.value]
- raise generic.ScriptError(
- "Undeclared block identifier '{}' encountered".format(block_name.value), block_name.pos
- )
+ raise generic.ScriptError(f"Undeclared block identifier '{block_name.value}' encountered", block_name.pos)
diff --git a/nml/ast/spriteblock.py b/nml/ast/spriteblock.py
index ad106c402..2081170bc 100644
--- a/nml/ast/spriteblock.py
+++ b/nml/ast/spriteblock.py
@@ -31,9 +31,7 @@ def pre_process(self):
for sprite in self.sprite_list:
if isinstance(sprite, real_sprite.TemplateUsage):
if sprite.name.value == self.name.value:
- raise generic.ScriptError(
- "Sprite template '{}' includes itself.".format(sprite.name.value), self.pos
- )
+ raise generic.ScriptError(f"Sprite template '{sprite.name.value}' includes itself.", self.pos)
elif sprite.name.value not in real_sprite.sprite_template_map:
raise generic.ScriptError(
"Encountered unknown template identifier: " + sprite.name.value, sprite.pos
@@ -43,9 +41,8 @@ def pre_process(self):
real_sprite.sprite_template_map[self.name.value] = self
else:
raise generic.ScriptError(
- "Template named '{}' is already defined, first definition at {}".format(
- self.name.value, real_sprite.sprite_template_map[self.name.value].pos
- ),
+ f"Template named '{self.name.value}' is already defined,"
+ f" first definition at {real_sprite.sprite_template_map[self.name.value].pos}",
self.pos,
)
@@ -56,7 +53,7 @@ def get_labels(self):
sprite_labels, num_sprites = sprite.get_labels()
for lbl, lbl_offset in sprite_labels.items():
if lbl in labels:
- raise generic.ScriptError("Duplicate label encountered; '{}' already exists.".format(lbl), self.pos)
+ raise generic.ScriptError(f"Duplicate label encountered; '{lbl}' already exists.", self.pos)
labels[lbl] = lbl_offset + offset
offset += num_sprites
return labels, offset
@@ -76,7 +73,7 @@ def get_action_list(self):
def __str__(self):
ret = "template {}({}) {{\n".format(str(self.name), ", ".join([str(param) for param in self.param_list]))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(sprite)
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
@@ -151,7 +148,7 @@ def pre_process(self):
sprite_labels, num_sprites = sprite.get_labels()
for lbl, lbl_offset in sprite_labels.items():
if lbl in self.labels:
- raise generic.ScriptError("Duplicate label encountered; '{}' already exists.".format(lbl), self.pos)
+ raise generic.ScriptError(f"Duplicate label encountered; '{lbl}' already exists.", self.pos)
self.labels[lbl] = lbl_offset + offset
offset += num_sprites
@@ -184,7 +181,7 @@ def __str__(self):
params.append(self.mask_file)
ret = "spriteset({}) {{\n".format(", ".join(str(p) for p in params))
for sprite in self.sprite_list:
- ret += "\t{}\n".format(str(sprite))
+ ret += f"\t{sprite}\n"
ret += "}\n"
return ret
@@ -219,9 +216,9 @@ def get_action_list(self):
return action_list
def __str__(self):
- ret = "spritegroup {} {{\n".format(self.name)
+ ret = f"spritegroup {self.name} {{\n"
for spriteview in self.spriteview_list:
- ret += "\t{}\n".format(spriteview)
+ ret += f"\t{spriteview}\n"
ret += "}\n"
return ret
@@ -275,7 +272,7 @@ def pre_process(self):
if not isinstance(param, expression.Identifier):
raise generic.ScriptError("spritelayout parameter names must be identifiers.", param.pos)
if param.value in seen_names:
- raise generic.ScriptError("Duplicate parameter name '{}' encountered.".format(param.value), param.pos)
+ raise generic.ScriptError(f"Duplicate parameter name '{param.value}' encountered.", param.pos)
seen_names.add(param.value)
spritelayout_base_class.pre_process(self)
diff --git a/nml/ast/switch.py b/nml/ast/switch.py
index 0bacd0b5c..e376b328d 100644
--- a/nml/ast/switch.py
+++ b/nml/ast/switch.py
@@ -38,7 +38,7 @@ def __init__(self, param_list, body, pos):
self.var_range = var_ranges[param_list[1].value]
else:
raise generic.ScriptError(
- "Unrecognized value for switch parameter 2 'variable range': '{}'".format(param_list[1].value),
+ f"Unrecognized value for switch parameter 2 'variable range': '{param_list[1].value}'",
param_list[1].pos,
)
if not isinstance(param_list[2], expression.Identifier):
@@ -58,7 +58,7 @@ def pre_process(self):
if not isinstance(param, expression.Identifier):
raise generic.ScriptError("switch parameter names must be identifiers.", param.pos)
if param.value in seen_names:
- raise generic.ScriptError("Duplicate parameter name '{}' encountered.".format(param.value), param.pos)
+ raise generic.ScriptError(f"Duplicate parameter name '{param.value}' encountered.", param.pos)
seen_names.add(param.value)
feature = next(iter(self.feature_set))
@@ -112,7 +112,7 @@ def optimise(self):
if self.optimised:
generic.print_warning(
generic.Warning.OPTIMISATION,
- "Block '{}' returns a constant, optimising.".format(self.name.value),
+ f"Block '{self.name.value}' returns a constant, optimising.",
self.pos,
)
return self.optimised is not self
@@ -129,15 +129,12 @@ def collect_references(self):
def is_read_only(self):
for result in [r.result for r in self.body.ranges] + [self.body.default]:
- if result is not None and result.value is not None:
- if not result.value.is_read_only():
- return False
+ if result is not None and result.value is not None and not result.value.is_read_only():
+ return False
return self.expr.is_read_only()
def debug_print(self, indentation):
- generic.print_dbg(
- indentation, "Switch, Feature = {:d}, name = {}".format(next(iter(self.feature_set)), self.name.value)
- )
+ generic.print_dbg(indentation, f"Switch, Feature = {next(iter(self.feature_set))}, name = {self.name.value}")
if self.param_list:
generic.print_dbg(indentation + 2, "Parameters:")
@@ -158,8 +155,8 @@ def get_action_list(self):
def __str__(self):
var_range = "SELF" if self.var_range == 0x89 else "PARENT"
params = "" if not self.param_list else "{}, ".format(", ".join(str(x) for x in self.param_list))
- return "switch({}, {}, {}, {}{}) {{\n{}}}\n".format(
- next(iter(self.feature_set)), var_range, self.name, params, self.expr, self.body
+ return (
+ f"switch({next(iter(self.feature_set))}, {var_range}, {self.name}, {params}{self.expr}) {{\n{self.body}}}\n"
)
@@ -213,9 +210,9 @@ def debug_print(self, indentation):
self.default.debug_print(indentation + 2)
def __str__(self):
- ret = "".join("\t{}\n".format(r) for r in self.ranges)
+ ret = "".join(f"\t{r}\n" for r in self.ranges)
if self.default is not None:
- ret += "\t{}\n".format(str(self.default))
+ ret += f"\t{self.default}\n"
return ret
@@ -287,18 +284,16 @@ def __str__(self):
assert self.is_return
return "return;"
elif self.is_return:
- return "return {};".format(self.value)
+ return f"return {self.value};"
else:
- return "{};".format(self.value)
+ return f"{self.value};"
class RandomSwitch(switch_base_class):
def __init__(self, param_list, choices, pos):
base_statement.BaseStatement.__init__(self, "random_switch-block", pos, False, False)
if not (3 <= len(param_list) <= 4):
- raise generic.ScriptError(
- "random_switch requires 3 or 4 parameters, encountered {:d}".format(len(param_list)), pos
- )
+ raise generic.ScriptError(f"random_switch requires 3 or 4 parameters, encountered {len(param_list)}", pos)
# feature
feature = general.parse_feature(param_list[0]).value
@@ -373,7 +368,7 @@ def pre_process(self):
spritegroup = action2.resolve_spritegroup(dep_list[i].name)
if not isinstance(spritegroup, RandomSwitch):
raise generic.ScriptError(
- "Value of (in)dependent '{}' should refer to a random_switch.".format(dep_list[i].name.value),
+ f"Value of (in)dependent '{dep_list[i].name.value}' should refer to a random_switch.",
dep_list[i].pos,
)
@@ -401,7 +396,7 @@ def optimise(self):
):
generic.print_warning(
generic.Warning.OPTIMISATION,
- "Block '{}' returns a constant, optimising.".format(self.name.value),
+ f"Block '{self.name.value}' returns a constant, optimising.",
self.pos,
)
self.optimised = optimised
@@ -417,10 +412,7 @@ def collect_references(self):
return all_refs
def is_read_only(self):
- for choice in self.choices:
- if not choice.result.value.is_read_only():
- return False
- return True
+ return all(choice.result.value.is_read_only() for choice in self.choices)
def debug_print(self, indentation):
generic.print_dbg(indentation, "Random")
@@ -448,13 +440,11 @@ def get_action_list(self):
return []
def __str__(self):
- ret = "random_switch({}, {}, {}, {}) {{\n".format(
- str(next(iter(self.feature_set))), str(self.type), str(self.name), str(self.triggers)
- )
+ ret = f"random_switch({next(iter(self.feature_set))}, {self.type}, {self.name}, {self.triggers}) {{\n"
for dep in self.dependent:
- ret += "dependent: {};\n".format(dep)
+ ret += f"dependent: {dep};\n"
for indep in self.independent:
- ret += "independent: {};\n".format(indep)
+ ret += f"independent: {indep};\n"
for choice in self.choices:
ret += str(choice) + "\n"
ret += "}\n"
@@ -495,4 +485,4 @@ def debug_print(self, indentation):
self.result.debug_print(indentation + 2)
def __str__(self):
- return "{}: {}".format(self.probability, self.result)
+ return f"{self.probability}: {self.result}"
diff --git a/nml/ast/tilelayout.py b/nml/ast/tilelayout.py
index 0b59c3405..37ffdb6b5 100644
--- a/nml/ast/tilelayout.py
+++ b/nml/ast/tilelayout.py
@@ -50,7 +50,7 @@ def pre_process(self):
if isinstance(tileprop, assignment.Assignment):
name = tileprop.name.value
if name in self.properties:
- raise generic.ScriptError("Duplicate property {} in tile layout".format(name), tileprop.name.pos)
+ raise generic.ScriptError(f"Duplicate property {name} in tile layout", tileprop.name.pos)
self.properties[name] = tileprop.value.reduce_constant(global_constants.const_list)
else:
assert isinstance(tileprop, LayoutTile)
@@ -61,15 +61,13 @@ def pre_process(self):
tile = expression.ConstantNumeric(0xFF)
self.tile_list.append(LayoutTile(x, y, tile))
if self.name in action0properties.tilelayout_names:
- raise generic.ScriptError(
- "A tile layout with name '{}' has already been defined.".format(self.name), self.pos
- )
+ raise generic.ScriptError(f"A tile layout with name '{self.name}' has already been defined.", self.pos)
action0properties.tilelayout_names[self.name] = self
def debug_print(self, indentation):
generic.print_dbg(indentation, "TileLayout")
for tile in self.tile_list:
- generic.print_dbg(indentation + 2, "At {:d},{:d}:".format(tile.x, tile.y))
+ generic.print_dbg(indentation + 2, f"At {tile.x},{tile.y}:")
tile.tiletype.debug_print(indentation + 4)
def get_action_list(self):
@@ -101,9 +99,7 @@ def write(self, file):
tile_id = global_constants.item_names[tile.tiletype.value].id
if not isinstance(tile_id, expression.ConstantNumeric):
raise generic.ScriptError(
- "Tile '{}' cannot be used in a tilelayout, as its ID is not a constant.".format(
- tile.tiletype.value
- ),
+ f"Tile '{tile.tiletype.value}' cannot be used in a tilelayout, as its ID is not a constant.",
tile.tiletype.pos,
)
file.print_wordx(tile_id.value)
@@ -133,4 +129,4 @@ def __init__(self, x, y, tiletype):
self.tiletype = tiletype
def __str__(self):
- return "{}, {}: {};".format(self.x, self.y, self.tiletype)
+ return f"{self.x}, {self.y}: {self.tiletype};"
diff --git a/nml/ast/townnames.py b/nml/ast/townnames.py
index 3370fd713..1df864174 100644
--- a/nml/ast/townnames.py
+++ b/nml/ast/townnames.py
@@ -95,13 +95,13 @@ def pre_process(self):
raise generic.ScriptError("ID must be a number between 0 and 0x7f (inclusive)", self.pos)
if self.id_number not in actionF.free_numbers:
- raise generic.ScriptError("town names ID 0x{:x} is already used.".format(self.id_number), self.pos)
+ raise generic.ScriptError(f"town names ID 0x{self.id_number:x} is already used.", self.pos)
actionF.free_numbers.remove(self.id_number)
def __str__(self):
ret = "town_names"
if self.name is not None:
- ret += "({})".format(self.name)
+ ret += f"({self.name})"
ret += "{{\n{}}}\n".format("".join(str(x) for x in self.param_list))
return ret
@@ -156,9 +156,7 @@ def make_actions(self):
if len(self.pieces) == 0:
raise generic.ScriptError("Expected names and/or town_name references in the part.", self.pos)
if len(self.pieces) > 255:
- raise generic.ScriptError(
- "Too many values in a part, found {:d}, maximum is 255".format(len(self.pieces)), self.pos
- )
+ raise generic.ScriptError(f"Too many values in a part, found {len(self.pieces)}, maximum is 255", self.pos)
return actFs, self
@@ -218,7 +216,7 @@ def move_pieces(self):
# Assign to action F
actFs = []
for _prob, _idx, sub in finished_actions:
- actF_name = expression.Identifier("**townname #{:d}**".format(townname_serial), None)
+ actF_name = expression.Identifier(f"**townname #{townname_serial}**", None)
townname_serial = townname_serial + 1
town_part = TownNamesPart(sub, self.pos)
town_part.set_numbits(num_bits)
@@ -268,7 +266,7 @@ def set_numbits(self, numbits):
def debug_print(self, indentation):
total = sum(piece.probability.value for piece in self.pieces)
- generic.print_dbg(indentation, "Town names part (total {:d})".format(total))
+ generic.print_dbg(indentation, f"Town names part (total {total})")
for piece in self.pieces:
piece.debug_print(indentation + 2, total)
@@ -322,7 +320,7 @@ def debug_print(self, indentation):
self.value.debug_print(indentation + 4)
def __str__(self):
- return "{}: {};\n".format(self.key, self.value)
+ return f"{self.key}: {self.value};\n"
class TownNamesEntryDefinition:
@@ -369,15 +367,15 @@ def debug_print(self, indentation, total):
if isinstance(self.def_number, expression.Identifier):
name_text = "name '" + self.def_number.value + "'"
else:
- name_text = "number 0x{:x}".format(self.def_number.value)
+ name_text = f"number 0x{self.def_number.value:x}"
generic.print_dbg(
indentation,
- "Insert town_name ID {} with probability {:d}/{:d}".format(name_text, self.probability.value, total),
+ f"Insert town_name ID {name_text} with probability {self.probability.value}/{total}",
)
def __str__(self):
- return "town_names({}, {:d}),".format(str(self.def_number), self.probability.value)
+ return f"town_names({self.def_number}, {self.probability.value}),"
def get_length(self):
return 2
@@ -392,16 +390,14 @@ def resolve_townname_id(self):
self.number = actionF.named_numbers.get(self.def_number.value)
if self.number is None:
raise generic.ScriptError(
- 'Town names name "{}" is not defined or points to a next town_names node'.format(
- self.def_number.value
- ),
+ f'Town names name "{self.def_number.value}" is not defined or points to a next town_names node',
self.pos,
)
else:
self.number = self.def_number.value
if self.number not in actionF.numbered_numbers:
raise generic.ScriptError(
- 'Town names number "{}" is not defined or points to a next town_names node'.format(self.number),
+ f'Town names number "{self.number}" is not defined or points to a next town_names node',
self.pos,
)
return self.number
@@ -439,12 +435,10 @@ def pre_process(self):
raise generic.ScriptError("Probability out of range (must be between 0 and 0x7f inclusive).", self.pos)
def debug_print(self, indentation, total):
- generic.print_dbg(
- indentation, "Text {} with probability {:d}/{:d}".format(self.text.value, self.probability.value, total)
- )
+ generic.print_dbg(indentation, f"Text {self.text.value} with probability {self.probability.value}/{total}")
def __str__(self):
- return "text({}, {:d}),".format(self.text, self.probability.value)
+ return f"text({self.text}, {self.probability.value}),"
def get_length(self):
return 1 + grfstrings.get_string_size(self.text.value) # probability, text
diff --git a/nml/editors/extract_tables.py b/nml/editors/extract_tables.py
index 528b7429c..1bc35f916 100644
--- a/nml/editors/extract_tables.py
+++ b/nml/editors/extract_tables.py
@@ -71,7 +71,7 @@
variables = set()
for d in var_tables:
- for key in d.keys():
+ for key in d:
variables.add(key)
prop_tables = [
@@ -97,7 +97,7 @@
properties = set()
for d in prop_tables:
- for key in d.keys():
+ for key in d:
properties.add(key)
dummy = action2layout.Action2LayoutSprite(None, None)
@@ -126,7 +126,7 @@
callbacks = set()
for d in cb_tables:
- for key in d.keys():
+ for key in d:
callbacks.add(key)
# No easy way to get action14 stuff
@@ -169,7 +169,7 @@
constant_names = set()
for d in const_tables:
- for key in d.keys():
+ for key in d:
constant_names.add(key)
act5_names = set(action5.action5_table.keys())
diff --git a/nml/editors/kate.py b/nml/editors/kate.py
index 454720062..3b7f718bc 100644
--- a/nml/editors/kate.py
+++ b/nml/editors/kate.py
@@ -163,19 +163,19 @@ def write_file(fname):
with open(fname, "w") as file:
file.write(header_text)
for word in extract_tables.keywords:
- file.write(" - {}
\n".format(word))
+ file.write(f" - {word}
\n")
file.write(feature_text)
for word in extract_tables.features:
- file.write(" - {}
\n".format(word))
+ file.write(f" - {word}
\n")
file.write(builtin_text)
for word in extract_tables.functions:
- file.write(" - {}
\n".format(word))
+ file.write(f" - {word}
\n")
file.write(constant_text)
for word in extract_tables.callback_names_table:
- file.write(" - {}
\n".format(word))
+ file.write(f" - {word}
\n")
file.write(tail_text)
diff --git a/nml/editors/visualstudiocode.py b/nml/editors/visualstudiocode.py
index 3c9ad2191..af61505d4 100644
--- a/nml/editors/visualstudiocode.py
+++ b/nml/editors/visualstudiocode.py
@@ -12,6 +12,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
import json
+
from nml.editors import extract_tables
text1 = """\
diff --git a/nml/expression/__init__.py b/nml/expression/__init__.py
index 4cef1ef81..ffbe7d6a7 100644
--- a/nml/expression/__init__.py
+++ b/nml/expression/__init__.py
@@ -15,6 +15,7 @@
import re
+from .abs_op import AbsOp
from .array import Array
from .base_expression import ConstantFloat, ConstantNumeric, Expression, Type
from .bin_not import BinNot, Not
@@ -33,10 +34,41 @@
from .string import String
from .string_literal import StringLiteral
from .ternaryop import TernaryOp
-from .abs_op import AbsOp
from .variable import Variable
-is_valid_id = re.compile("[a-zA-Z_][a-zA-Z0-9_]{3}$")
+__all__ = [
+ "AbsOp",
+ "AcceptCargo",
+ "Array",
+ "BinNot",
+ "BinOp",
+ "BitMask",
+ "Boolean",
+ "ConstantFloat",
+ "ConstantNumeric",
+ "Expression",
+ "FunctionCall",
+ "FunctionPtr",
+ "GRMOp",
+ "Identifier",
+ "Not",
+ "OtherGRFParameter",
+ "Parameter",
+ "PatchVariable",
+ "ProduceCargo",
+ "SpecialCheck",
+ "SpecialParameter",
+ "SpriteGroupRef",
+ "StorageOp",
+ "String",
+ "StringLiteral",
+ "TernaryOp",
+ "Type",
+ "Variable",
+ "parse_string_to_dword",
+]
+
+is_valid_id = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]{3}$")
def identifier_to_print(name):
@@ -49,4 +81,4 @@ def identifier_to_print(name):
"""
if is_valid_id.match(name):
return name
- return '"{}"'.format(name)
+ return f'"{name}"'
diff --git a/nml/expression/base_expression.py b/nml/expression/base_expression.py
index b6580ad28..b2cee5895 100644
--- a/nml/expression/base_expression.py
+++ b/nml/expression/base_expression.py
@@ -46,7 +46,7 @@ def debug_print(self, indentation):
@param indentation: Indent all printed lines with at least
C{indentation} spaces.
"""
- raise NotImplementedError("debug_print must be implemented in expression-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"debug_print must be implemented in expression-subclass {type(self)!r}")
def __str__(self):
"""
@@ -54,7 +54,7 @@ def __str__(self):
@return: A string representation of this expression.
"""
- raise NotImplementedError("__str__ must be implemented in expression-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"__str__ must be implemented in expression-subclass {type(self)!r}")
def reduce(self, id_dicts=None, unknown_id_fatal=True):
"""
@@ -67,7 +67,7 @@ def reduce(self, id_dicts=None, unknown_id_fatal=True):
@return: A deep copy of this expression simplified as much as possible.
"""
- raise NotImplementedError("reduce must be implemented in expression-subclass {!r}".format(type(self)))
+ raise NotImplementedError(f"reduce must be implemented in expression-subclass {type(self)!r}")
def reduce_constant(self, id_dicts=None):
"""
diff --git a/nml/expression/binop.py b/nml/expression/binop.py
index fe83c0a45..9c0601cf0 100644
--- a/nml/expression/binop.py
+++ b/nml/expression/binop.py
@@ -225,9 +225,9 @@ def reduce(self, id_dicts=None, unknown_id_fatal=True):
def supported_by_action2(self, raise_error):
if not self.op.act2_supports:
- token = " '{}'".format(self.op.token) if self.op.token else ""
+ token = f" '{self.op.token}'" if self.op.token else ""
if raise_error:
- raise generic.ScriptError("Operator{} not supported in a switch-block".format(token), self.pos)
+ raise generic.ScriptError(f"Operator{token} not supported in a switch-block", self.pos)
return False
return self.expr1.supported_by_action2(raise_error) and self.expr2.supported_by_action2(raise_error)
@@ -240,8 +240,8 @@ def supported_by_actionD(self, raise_error):
raise generic.ScriptError("STORE_TEMP is only available in switch-blocks.", self.pos)
# default case
- token = " '{}'".format(self.op.token) if self.op.token else ""
- raise generic.ScriptError("Operator{} not supported in parameter assignment".format(token), self.pos)
+ token = f" '{self.op.token}'" if self.op.token else ""
+ raise generic.ScriptError(f"Operator{token} not supported in parameter assignment", self.pos)
return False
return self.expr1.supported_by_actionD(raise_error) and self.expr2.supported_by_actionD(raise_error)
diff --git a/nml/expression/boolean.py b/nml/expression/boolean.py
index e80f3712a..1d45b26cc 100644
--- a/nml/expression/boolean.py
+++ b/nml/expression/boolean.py
@@ -60,4 +60,4 @@ def is_boolean(self):
return True
def __str__(self):
- return "!!({})".format(self.expr)
+ return f"!!({self.expr})"
diff --git a/nml/expression/cargo.py b/nml/expression/cargo.py
index 4530609b7..b1ce15e54 100644
--- a/nml/expression/cargo.py
+++ b/nml/expression/cargo.py
@@ -33,13 +33,13 @@ def cargolabel(self):
def debug_print(self, indentation):
if self.value is None:
- generic.print_dbg(indentation, "{0} cargo {1}".format(self._debugname, self.cargolabel()))
+ generic.print_dbg(indentation, f"{self._debugname} cargo {self.cargolabel()}")
else:
- generic.print_dbg(indentation, "{0} cargo {1} with result:".format(self._debugname, self.cargolabel()))
+ generic.print_dbg(indentation, f"{self._debugname} cargo {self.cargolabel()} with result:")
self.value.debug_print(indentation + 2)
def __str__(self):
- return "{0}({1}, {2})".format(self._fnname, self.cargolabel(), str(self.value))
+ return f"{self._fnname}({self.cargolabel()}, {self.value})"
def reduce(self, id_dicts=None, unknown_id_fatal=True):
return self
diff --git a/nml/expression/functioncall.py b/nml/expression/functioncall.py
index c8a4ec4af..51a0ecc73 100644
--- a/nml/expression/functioncall.py
+++ b/nml/expression/functioncall.py
@@ -21,13 +21,13 @@
from nml import generic, global_constants, nmlop
from . import identifier
+from .abs_op import AbsOp
from .base_expression import ConstantFloat, ConstantNumeric, Expression, Type
from .bitmask import BitMask
from .cargo import AcceptCargo, ProduceCargo
from .parameter import parse_string_to_dword
from .storage_op import StorageOp
from .string_literal import StringLiteral
-from .abs_op import AbsOp
class FunctionCall(Expression):
@@ -64,12 +64,10 @@ def reduce(self, id_dicts=None, unknown_id_fatal=True):
func_ptr.is_procedure = True
return func_ptr
if func_ptr.type() != Type.FUNCTION_PTR:
- raise generic.ScriptError(
- "'{}' is defined, but it is not a function.".format(self.name.value), self.pos
- )
+ raise generic.ScriptError(f"'{self.name.value}' is defined, but it is not a function.", self.pos)
return func_ptr.call(params)
if unknown_id_fatal:
- raise generic.ScriptError("'{}' is not defined as a function.".format(self.name.value), self.pos)
+ raise generic.ScriptError(f"'{self.name.value}' is not defined as a function.", self.pos)
return FunctionCall(self.name, params, self.pos)
@@ -231,7 +229,7 @@ def builtin_date(name, args, pos):
for i in range(month - 1):
day_in_year += days_in_month[i]
day_in_year += day
- if month >= 3 and (year.value % 4 == 0) and ((not year.value % 100 == 0) or (year.value % 400 == 0)):
+ if month >= 3 and (year.value % 4 == 0) and (year.value % 100 != 0 or year.value % 400 == 0):
day_in_year += 1
return ConstantNumeric(year.value * 365 + calendar.leapdays(0, year.value) + day_in_year - 1, pos)
@@ -259,7 +257,7 @@ def builtin_day_of_year(name, args, pos):
# Mapping of month to number of days in that month.
number_days = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31}
if day.value < 1 or day.value > number_days[month.value]:
- raise generic.ScriptError("Day should be value between 1 and {:d}.".format(number_days[month.value]), day.pos)
+ raise generic.ScriptError(f"Day should be value between 1 and {number_days[month.value]}.", day.pos)
return ConstantNumeric(datetime.date(1, month.value, day.value).toordinal(), pos)
@@ -376,7 +374,7 @@ def builtin_typelabel_available(name, args, pos):
if len(args) != 1:
raise generic.ScriptError(name + "() must have exactly 1 parameter", pos)
label = args[0].reduce()
- return SpecialCheck(op, 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, label), pos=args[0].pos)
+ return SpecialCheck(op, 0, (0, 1), parse_string_to_dword(label), f"{name}({label})", pos=args[0].pos)
@builtins("grf_current_status", "grf_future_status", "grf_order_behind")
@@ -396,12 +394,12 @@ def builtin_grf_status(name, args, pos):
if len(args) == 1:
grfid = args[0].reduce()
mask = None
- string = "{}({})".format(name, grfid)
+ string = f"{name}({grfid})"
varsize = 4
elif len(args) == 2:
grfid = args[0].reduce()
mask = parse_string_to_dword(args[1].reduce())
- string = "{}({}, {})".format(name, grfid, mask)
+ string = f"{name}({grfid}, {mask})"
varsize = 8
else:
raise generic.ScriptError(name + "() must have 1 or 2 parameters", pos)
@@ -421,7 +419,7 @@ def builtin_visual_effect_and_powered(name, args, pos):
"""
arg_len = 2 if name == "visual_effect" else 3
if len(args) != arg_len:
- raise generic.ScriptError(name + "() must have {:d} parameters".format(arg_len), pos)
+ raise generic.ScriptError(name + f"() must have {arg_len} parameters", pos)
effect = args[0].reduce_constant(global_constants.const_list).value
offset = nmlop.ADD(args[1], 8).reduce_constant().value
generic.check_range(offset, 0, 0x0F, "offset in function " + name, pos)
@@ -494,7 +492,7 @@ def builtin_resolve_typelabel(name, args, pos, table_name=None):
raise generic.ScriptError(name + "() must have 1 parameter", pos)
if not isinstance(args[0], StringLiteral) or args[0].value not in table:
raise generic.ScriptError(
- "Parameter for {}() must be a string literal that is also in your {} table".format(name, table_name), pos
+ f"Parameter for {name}() must be a string literal that is also in your {table_name} table", pos
)
return ConstantNumeric(table[args[0].value])
@@ -504,7 +502,7 @@ def builtin_reserve_sprites(name, args, pos):
if len(args) != 1:
raise generic.ScriptError(name + "() must have 1 parameter", pos)
count = args[0].reduce_constant()
- return GRMOp(nmlop.GRM_RESERVE, 0x08, count.value, lambda x: "{}({:d})".format(name, count.value), pos)
+ return GRMOp(nmlop.GRM_RESERVE, 0x08, count.value, lambda x: f"{name}({count.value})", pos)
@builtin
@@ -631,9 +629,9 @@ def builtin_relative_coord(name, args, pos):
raise generic.ScriptError(name + "() must have x and y coordinates as parameters", pos)
if isinstance(args[0], ConstantNumeric):
- generic.check_range(args[0].value, 0, 255, "Argument of '{}'".format(name), args[0].pos)
+ generic.check_range(args[0].value, 0, 255, f"Argument of '{name}'", args[0].pos)
if isinstance(args[1], ConstantNumeric):
- generic.check_range(args[1].value, 0, 255, "Argument of '{}'".format(name), args[1].pos)
+ generic.check_range(args[1].value, 0, 255, f"Argument of '{name}'", args[1].pos)
x_coord = nmlop.AND(args[0], 0xFF)
y_coord = nmlop.AND(args[1], 0xFF)
@@ -679,7 +677,7 @@ def builtin_slope_to_sprite_offset(name, args, pos):
raise generic.ScriptError(name + "() must have 1 parameter", pos)
if isinstance(args[0], ConstantNumeric):
- generic.check_range(args[0].value, 0, 15, "Argument of '{}'".format(name), args[0].pos)
+ generic.check_range(args[0].value, 0, 15, f"Argument of '{name}'", args[0].pos)
# step 1: ((slope >= 0) & (slope <= 14)) * slope
# This handles all non-steep slopes
@@ -705,7 +703,7 @@ def builtin_palette_1cc(name, args, pos):
raise generic.ScriptError(name + "() must have 1 parameter", pos)
if isinstance(args[0], ConstantNumeric):
- generic.check_range(args[0].value, 0, 15, "Argument of '{}'".format(name), args[0].pos)
+ generic.check_range(args[0].value, 0, 15, f"Argument of '{name}'", args[0].pos)
return nmlop.ADD(args[0], 775, pos)
@@ -722,7 +720,7 @@ def builtin_palette_2cc(name, args, pos):
for i in range(0, 2):
if isinstance(args[i], ConstantNumeric):
- generic.check_range(args[i].value, 0, 15, "Argument of '{}'".format(name), args[i].pos)
+ generic.check_range(args[i].value, 0, 15, f"Argument of '{name}'", args[i].pos)
col2 = nmlop.MUL(args[1], 16, pos)
col12 = nmlop.ADD(col2, args[0])
@@ -744,7 +742,7 @@ def builtin_vehicle_curv_info(name, args, pos):
for arg in args:
if isinstance(arg, ConstantNumeric):
- generic.check_range(arg.value, -2, 2, "Argument of '{}'".format(name), arg.pos)
+ generic.check_range(arg.value, -2, 2, f"Argument of '{name}'", arg.pos)
args = [nmlop.AND(arg, 0xF, pos) for arg in args]
cur_next = nmlop.SHIFT_LEFT(args[1], 8)
@@ -771,7 +769,7 @@ def builtin_format_string(name, args, pos):
arg = arg.reduce()
if not isinstance(arg, (StringLiteral, ConstantFloat, ConstantNumeric)):
raise generic.ScriptError(
- name + "() parameter {:d} is not a constant number of literal string".format(i + 1), arg.pos
+ name + f"() parameter {i + 1} is not a constant number of literal string", arg.pos
)
format_args.append(arg.value)
@@ -779,7 +777,7 @@ def builtin_format_string(name, args, pos):
result = format.value % tuple(format_args)
return StringLiteral(result, pos)
except Exception as ex:
- raise generic.ScriptError("Invalid combination of format / arguments for {}: {}".format(name, str(ex)), pos)
+ raise generic.ScriptError(f"Invalid combination of format / arguments for {name}: {ex}", pos)
# }
diff --git a/nml/expression/functionptr.py b/nml/expression/functionptr.py
index 71596d1c4..e607aec25 100644
--- a/nml/expression/functionptr.py
+++ b/nml/expression/functionptr.py
@@ -51,7 +51,7 @@ def __str__(self):
def reduce(self, id_dicts=None, unknown_id_fatal=True):
raise generic.ScriptError(
- "'{}' is a function and should be called using the function call syntax.".format(str(self.name)),
+ f"'{self.name}' is a function and should be called using the function call syntax.",
self.name.pos,
)
diff --git a/nml/expression/identifier.py b/nml/expression/identifier.py
index 426bdb4b9..c020706dc 100644
--- a/nml/expression/identifier.py
+++ b/nml/expression/identifier.py
@@ -80,7 +80,7 @@ def reduce(self, id_dicts=None, unknown_id_fatal=True, search_func_ptr=False):
def supported_by_actionD(self, raise_error):
if raise_error:
- raise generic.ScriptError("Unknown identifier '{}'".format(self.value), self.pos)
+ raise generic.ScriptError(f"Unknown identifier '{self.value}'", self.pos)
return False
diff --git a/nml/expression/parameter.py b/nml/expression/parameter.py
index 265b46849..c8ef2933b 100644
--- a/nml/expression/parameter.py
+++ b/nml/expression/parameter.py
@@ -35,7 +35,7 @@ def debug_print(self, indentation):
self.num.debug_print(indentation + 2)
def __str__(self):
- return "param[{}]".format(self.num)
+ return f"param[{self.num}]"
def reduce(self, id_dicts=None, unknown_id_fatal=True):
num = self.num.reduce(id_dicts)
@@ -76,7 +76,7 @@ def debug_print(self, indentation):
self.num.debug_print(indentation + 2)
def __str__(self):
- return "param[{}, {}]".format(self.grfid, self.num)
+ return f"param[{self.grfid}, {self.num}]"
def reduce(self, id_dicts=None, unknown_id_fatal=True):
grfid = self.grfid.reduce(id_dicts)
diff --git a/nml/expression/patch_variable.py b/nml/expression/patch_variable.py
index 33f9d50b2..b0eeeac19 100644
--- a/nml/expression/patch_variable.py
+++ b/nml/expression/patch_variable.py
@@ -34,7 +34,7 @@ def debug_print(self, indentation):
generic.print_dbg(indentation, "PatchVariable:", self.num)
def __str__(self):
- return "PatchVariable({:d})".format(self.num)
+ return f"PatchVariable({self.num})"
def reduce(self, id_dicts=None, unknown_id_fatal=True):
return self
diff --git a/nml/expression/special_parameter.py b/nml/expression/special_parameter.py
index 038edb9f4..0b5ebe0bb 100644
--- a/nml/expression/special_parameter.py
+++ b/nml/expression/special_parameter.py
@@ -60,7 +60,7 @@ def __init__(self, name, info, write_func, read_func, is_bool, pos=None):
self.is_bool = is_bool
def debug_print(self, indentation):
- generic.print_dbg(indentation, "Special parameter '{}'".format(self.name))
+ generic.print_dbg(indentation, f"Special parameter '{self.name}'")
def __str__(self):
return self.name
diff --git a/nml/expression/spritegroup_ref.py b/nml/expression/spritegroup_ref.py
index 834726f92..7835551b8 100644
--- a/nml/expression/spritegroup_ref.py
+++ b/nml/expression/spritegroup_ref.py
@@ -75,7 +75,7 @@ def get_action2_id(self, feature):
try:
spritegroup = action2.resolve_spritegroup(self.name)
except generic.ScriptError:
- raise AssertionError("Illegal action2 reference '{}' encountered.".format(self.name.value))
+ raise AssertionError(f"Illegal action2 reference '{self.name.value}' encountered.")
return spritegroup.get_action2(feature).id
diff --git a/nml/expression/storage_op.py b/nml/expression/storage_op.py
index 00f957ced..7393db551 100644
--- a/nml/expression/storage_op.py
+++ b/nml/expression/storage_op.py
@@ -57,10 +57,8 @@ def __init__(self, name, args, pos=None):
if self.info["grfid"]:
arg_len += (arg_len[0] + 1,)
if len(args) not in arg_len:
- argstr = "{:d}".format(arg_len[0]) if len(arg_len) == 1 else "{}..{}".format(arg_len[0], arg_len[1])
- raise generic.ScriptError(
- "{} requires {} argument(s), encountered {:d}".format(name, argstr, len(args)), pos
- )
+ argstr = f"{arg_len[0]}" if len(arg_len) == 1 else f"{arg_len[0]}..{arg_len[1]}"
+ raise generic.ScriptError(f"{name} requires {argstr} argument(s), encountered {len(args)}", pos)
i = 0
if self.info["store"]:
@@ -114,7 +112,7 @@ def reduce(self, id_dicts=None, unknown_id_fatal=True):
raise generic.ProcCallSyntaxError(register.name, register.pos)
raise generic.ScriptError("Register to access must be an integer.", register.pos)
if isinstance(register, ConstantNumeric) and register.value > self.info["max"]:
- raise generic.ScriptError("Maximum register for {} is {:d}".format(self.name, self.info["max"]), self.pos)
+ raise generic.ScriptError(f"Maximum register for {self.name} is {self.info['max']}", self.pos)
if isinstance(register, ConstantNumeric) and register.value in self.info["reserved"]:
raise generic.ScriptError(
"Temporary registers from 128 to 255 are reserved for NML's internal calculations.", self.pos
@@ -134,7 +132,7 @@ def supported_by_action2(self, raise_error):
def supported_by_actionD(self, raise_error):
if raise_error:
- raise generic.ScriptError("{}() may only be used inside switch-blocks".format(self.name), self.pos)
+ raise generic.ScriptError(f"{self.name}() may only be used inside switch-blocks", self.pos)
return False
def collect_references(self):
diff --git a/nml/expression/string_literal.py b/nml/expression/string_literal.py
index 8d3dbeb62..ab69bbd8a 100644
--- a/nml/expression/string_literal.py
+++ b/nml/expression/string_literal.py
@@ -31,10 +31,10 @@ def __init__(self, value, pos):
self.value = value
def debug_print(self, indentation):
- generic.print_dbg(indentation, 'String literal: "{}"'.format(self.value))
+ generic.print_dbg(indentation, f'String literal: "{self.value}"')
def __str__(self):
- return '"{}"'.format(self.value)
+ return f'"{self.value}"'
def write(self, file, size):
assert grfstrings.get_string_size(self.value, False, True) == size
diff --git a/nml/expression/ternaryop.py b/nml/expression/ternaryop.py
index 2c704fa54..beea59418 100644
--- a/nml/expression/ternaryop.py
+++ b/nml/expression/ternaryop.py
@@ -92,4 +92,4 @@ def __hash__(self):
return hash((self.guard, self.expr1, self.expr2))
def __str__(self):
- return "({} ? {} : {})".format(str(self.guard), str(self.expr1), str(self.expr2))
+ return f"({self.guard} ? {self.expr1} : {self.expr2})"
diff --git a/nml/expression/variable.py b/nml/expression/variable.py
index fb8c871d8..8b8979167 100644
--- a/nml/expression/variable.py
+++ b/nml/expression/variable.py
@@ -45,17 +45,17 @@ def debug_print(self, indentation):
extra_param.debug_print(indentation + 4)
def __str__(self):
- num = "0x{:02X}".format(self.num.value) if isinstance(self.num, ConstantNumeric) else str(self.num)
- ret = "var[{}, {}, {}".format(num, self.shift, self.mask)
+ num = f"0x{self.num.value:02X}" if isinstance(self.num, ConstantNumeric) else str(self.num)
+ ret = f"var[{num}, {self.shift}, {self.mask}"
if self.param is not None:
- ret += ", {}".format(self.param)
+ ret += f", {self.param}"
ret += "]"
if self.add is not None:
- ret = "({} + {})".format(ret, self.add)
+ ret = f"({ret} + {self.add})"
if self.div is not None:
- ret = "({} / {})".format(ret, self.div)
+ ret = f"({ret} / {self.div})"
if self.mod is not None:
- ret = "({} % {})".format(ret, self.mod)
+ ret = f"({ret} % {self.mod})"
return ret
def reduce(self, id_dicts=None, unknown_id_fatal=True):
diff --git a/nml/generic.py b/nml/generic.py
index a39180bce..78640130d 100644
--- a/nml/generic.py
+++ b/nml/generic.py
@@ -171,7 +171,7 @@ def __init__(self, filename, line_start, includes=None):
self.line_start = line_start
def __str__(self):
- return '"{}", line {:d}'.format(self.filename, self.line_start)
+ return f'"{self.filename}", line {self.line_start}'
class PixelPosition(Position):
@@ -191,7 +191,7 @@ def __init__(self, filename, xpos, ypos):
self.ypos = ypos
def __str__(self):
- return '"{}" at [x: {:d}, y: {:d}]'.format(self.filename, self.xpos, self.ypos)
+ return f'"{self.filename}" at [x: {self.xpos}, y: {self.ypos}]'
class ImageFilePosition(Position):
@@ -206,7 +206,7 @@ def __init__(self, filename, pos=None):
Position.__init__(self, filename, poslist)
def __str__(self):
- return 'Image file "{}"'.format(self.filename)
+ return f'Image file "{self.filename}"'
class LanguageFilePosition(Position):
@@ -218,7 +218,7 @@ def __init__(self, filename):
Position.__init__(self, filename, [])
def __str__(self):
- return 'Language file "{}"'.format(self.filename)
+ return f'Language file "{self.filename}"'
class ScriptError(Exception):
@@ -254,7 +254,7 @@ def __init__(self, value, min_value, max_value, name, pos=None):
class ProcCallSyntaxError(ScriptError):
def __init__(self, name, pos=None):
- ScriptError.__init__(self, "Missing '()' after '{}'.".format(name), pos)
+ ScriptError.__init__(self, f"Missing '()' after '{name}'.", pos)
class ImageError(ScriptError):
@@ -275,7 +275,7 @@ def __init__(self, typestr, pos=None):
@param pos: Position of the error, if provided.
@type pos: C{None} or L{Position}
"""
- ScriptError.__init__(self, "A grf may contain only one {}.".format(typestr), pos)
+ ScriptError.__init__(self, f"A grf may contain only one {typestr}.", pos)
class OnlyOnce:
@@ -369,7 +369,7 @@ def clear_progress():
hide_progress()
if (progress_message is not None) and (verbosity_level >= VERBOSITY_TIMING):
- print("{} {:.1f} s".format(progress_message, time.process_time() - progress_start_time))
+ print(f"{progress_message} {time.process_time() - progress_start_time:.1f} s")
progress_message = None
progress_start_time = None
@@ -526,29 +526,25 @@ def find_file(filepath):
matches = [entry for entry in entries if lcomp == entry.lower()]
if len(matches) == 0:
- raise ScriptError(
- 'Path "{}" does not exist (even after case conversions)'.format(os.path.join(path, comp))
- )
+ raise ScriptError(f'Path "{os.path.join(path, comp)}" does not exist (even after case conversions)')
elif len(matches) > 1:
raise ScriptError(
- 'Path "{}" is not unique (case conversion gave {:d} solutions)'.format(
- os.path.join(path, comp), len(matches)
- )
+ f'Path "{os.path.join(path, comp)}" is not unique (case conversion gave {len(matches)} solutions)'
)
if matches[0] != comp:
given_path = os.path.join(path, comp)
real_path = os.path.join(path, matches[0])
msg = (
- 'Path "{}" at the file system does not match path "{}" given in the input'
+ f'Path "{real_path}" at the file system does not match path "{given_path}" given in the input'
" (case mismatch in the last component)"
- ).format(real_path, given_path)
+ )
print_warning(Warning.GENERIC, msg)
elif os.access(path, os.X_OK):
# Path is only accessible, cannot inspect the file system.
matches = [comp]
else:
- raise ScriptError('Path "{}" does not exist or is not accessible'.format(path))
+ raise ScriptError(f'Path "{path}" does not exist or is not accessible')
path = os.path.join(path, matches[0])
if len(components) > 0:
@@ -613,6 +609,6 @@ def open_cache_file(sources, extension, mode):
if "w" in mode:
print_warning(
Warning.GENERIC,
- "Can't create cache file {}. Check permissions, or use --cache-dir or --no-cache.".format(path),
+ f"Can't create cache file {path}. Check permissions, or use --cache-dir or --no-cache.",
)
raise
diff --git a/nml/global_constants.py b/nml/global_constants.py
index 7bf9f38c4..62e7d2b5e 100644
--- a/nml/global_constants.py
+++ b/nml/global_constants.py
@@ -19,7 +19,7 @@
def constant_number(name, info, pos):
if isinstance(info, str):
generic.print_warning(
- generic.Warning.DEPRECATION, "'{}' is deprecated, consider using '{}' instead".format(name, info), pos
+ generic.Warning.DEPRECATION, f"'{name}' is deprecated, consider using '{info}' instead", pos
)
info = constant_numbers[info]
return expression.ConstantNumeric(info, pos)
@@ -1222,7 +1222,7 @@ def signextend(param, info):
def global_param_write(info, expr, pos):
- if not ("writable" in info and info["writable"]):
+ if not (info.get("writable")):
raise generic.ScriptError("Target parameter is not writable.", pos)
return expression.Parameter(expression.ConstantNumeric(info["num"]), pos), expr
@@ -1369,7 +1369,7 @@ def patch_variable(name, info, pos):
def config_flag_read(bit, pos):
- return expression.SpecialCheck((0x01, r"\70"), 0x85, (0, 1), bit, "PatchFlag({})".format(bit), varsize=1, pos=pos)
+ return expression.SpecialCheck((0x01, r"\70"), 0x85, (0, 1), bit, f"PatchFlag({bit})", varsize=1, pos=pos)
def config_flag(name, info, pos):
@@ -1422,7 +1422,7 @@ def setting_from_info(name, info, pos):
def item_to_id(name, item, pos):
if not isinstance(item.id, expression.ConstantNumeric):
- raise generic.ScriptError("Referencing item '{}' with a non-constant id is not possible.".format(name), pos)
+ raise generic.ScriptError(f"Referencing item '{name}' with a non-constant id is not possible.", pos)
return expression.ConstantNumeric(item.id.value, pos)
@@ -1503,10 +1503,10 @@ def print_stats():
"""
if len(cargo_numbers) > 0:
# Ids FE and FF have special meanings in Action3, so we do not consider them valid ids.
- generic.print_info("Cargo translation table: {}/{}".format(len(cargo_numbers), 0xFE))
+ generic.print_info(f"Cargo translation table: {len(cargo_numbers)}/{0xFE}")
if not is_default_railtype_table:
- generic.print_info("Railtype translation table: {}/{}".format(len(railtype_table), 0x100))
+ generic.print_info(f"Railtype translation table: {len(railtype_table)}/{0x100}")
if not is_default_roadtype_table:
- generic.print_info("Roadtype translation table: {}/{}".format(len(roadtype_table), 0x100))
+ generic.print_info(f"Roadtype translation table: {len(roadtype_table)}/{0x100}")
if not is_default_tramtype_table:
- generic.print_info("Tramtype translation table: {}/{}".format(len(tramtype_table), 0x100))
+ generic.print_info(f"Tramtype translation table: {len(tramtype_table)}/{0x100}")
diff --git a/nml/grfstrings.py b/nml/grfstrings.py
index 3a67a013a..1d796602e 100644
--- a/nml/grfstrings.py
+++ b/nml/grfstrings.py
@@ -43,7 +43,7 @@ def validate_string(string):
@type string: L{expression.String}
"""
if string.name.value not in default_lang.strings:
- raise generic.ScriptError('Unknown string "{}"'.format(string.name.value), string.pos)
+ raise generic.ScriptError(f'Unknown string "{string.name.value}"', string.pos)
def is_ascii_string(string):
@@ -179,7 +179,7 @@ def com_parse_comma(val, lang_id):
def com_parse_hex(val, lang_id):
val = val.reduce_constant()
- return "0x{:X}".format(val.value)
+ return f"0x{val.value:X}"
def com_parse_string(val, lang_id):
@@ -190,7 +190,7 @@ def com_parse_string(val, lang_id):
if isinstance(val, nml.expression.String):
# Check that the string exists
if val.name.value not in default_lang.strings:
- raise generic.ScriptError('Substring "{}" does not exist'.format(val.name.value), val.pos)
+ raise generic.ScriptError(f'Substring "{val.name.value}" does not exist', val.pos)
return get_translation(val, lang_id)
return val.value
@@ -298,7 +298,7 @@ def read_extra_commands(custom_tags_file):
# Failed to open custom_tags.txt, ignore this
return
line_no = 0
- with open(generic.find_file(custom_tags_file), "r", encoding="utf-8") as fh:
+ with open(generic.find_file(custom_tags_file), encoding="utf-8") as fh:
for line in fh:
line_no += 1
line = line.strip()
@@ -389,9 +389,8 @@ def validate_arguments(self, lang):
raise generic.ScriptError("Using {P} without a ##plural pragma", self.pos)
if len(self.arguments) != lang.get_num_plurals():
raise generic.ScriptError(
- "Invalid number of arguments to plural command, expected {:d} but got {:d}".format(
- lang.get_num_plurals(), len(self.arguments)
- ),
+ "Invalid number of arguments to plural command,"
+ f" expected {lang.get_num_plurals()} but got {len(self.arguments)}",
self.pos,
)
elif self.name == "G":
@@ -399,9 +398,8 @@ def validate_arguments(self, lang):
raise generic.ScriptError("Using {G} without a ##gender pragma", self.pos)
if len(self.arguments) != len(lang.genders):
raise generic.ScriptError(
- "Invalid number of arguments to gender command, expected {:d} but got {:d}".format(
- len(lang.genders), len(self.arguments)
- ),
+ "Invalid number of arguments to gender command,"
+ f" expected {len(lang.genders)} but got {len(self.arguments)}",
self.pos,
)
elif self.name == "G=":
@@ -409,13 +407,11 @@ def validate_arguments(self, lang):
raise generic.ScriptError("Using {G=} without a ##gender pragma", self.pos)
if len(self.arguments) != 1:
raise generic.ScriptError(
- "Invalid number of arguments to set-gender command, expected {:d} but got {:d}".format(
- 1, len(self.arguments)
- ),
+ f"Invalid number of arguments to set-gender command, expected 1 but got {len(self.arguments)}",
self.pos,
)
elif len(self.arguments) != 0:
- raise generic.ScriptError('Unexpected arguments to command "{}"'.format(self.name), self.pos)
+ raise generic.ScriptError(f'Unexpected arguments to command "{self.name}"', self.pos)
def parse_string(self, str_type, lang, wanted_lang_id, prev_command, stack, static_args):
"""
@@ -452,7 +448,7 @@ def parse_string(self, str_type, lang, wanted_lang_id, prev_command, stack, stat
if self.str_pos < len(static_args):
if "parse" not in commands[self.name]:
raise generic.ScriptError(
- "Provided a static argument for string command '{}' which is invalid".format(self.name),
+ f"Provided a static argument for string command '{self.name}' which is invalid",
self.pos,
)
# Parse commands using the wanted (not current) lang id, so translations are used if present
@@ -460,7 +456,7 @@ def parse_string(self, str_type, lang, wanted_lang_id, prev_command, stack, stat
prefix = ""
suffix = ""
if self.case:
- prefix += STRING_SELECT_CASE[str_type] + "\\{:02X}".format(self.case)
+ prefix += STRING_SELECT_CASE[str_type] + f"\\{self.case:02X}"
if stack_pos + self_size > 8:
raise generic.ScriptError(
"Trying to read an argument from the stack without reading the arguments before", self.pos
@@ -503,29 +499,29 @@ def parse_string(self, str_type, lang, wanted_lang_id, prev_command, stack, stat
if self.name == "P":
if offset < 0:
return self.arguments[lang.static_plural_form(static_args[offset]) - 1]
- ret = BEGIN_PLURAL_CHOICE_LIST[str_type] + "\\{:02X}".format(0x80 + offset)
+ ret = BEGIN_PLURAL_CHOICE_LIST[str_type] + f"\\{0x80 + offset:02X}"
for idx, arg in enumerate(self.arguments):
if idx == len(self.arguments) - 1:
ret += CHOICE_LIST_DEFAULT[str_type]
else:
- ret += CHOICE_LIST_ITEM[str_type] + "\\{:02X}".format(idx + 1)
+ ret += CHOICE_LIST_ITEM[str_type] + f"\\{idx + 1:02X}"
ret += arg
ret += CHOICE_LIST_END[str_type]
return ret
if self.name == "G":
if offset < 0:
return self.arguments[lang.static_gender(static_args[offset]) - 1]
- ret = BEGIN_GENDER_CHOICE_LIST[str_type] + "\\{:02X}".format(0x80 + offset)
+ ret = BEGIN_GENDER_CHOICE_LIST[str_type] + f"\\{0x80 + offset:02X}"
for idx, arg in enumerate(self.arguments):
if idx == len(self.arguments) - 1:
ret += CHOICE_LIST_DEFAULT[str_type]
else:
- ret += CHOICE_LIST_ITEM[str_type] + "\\{:02X}".format(idx + 1)
+ ret += CHOICE_LIST_ITEM[str_type] + f"\\{idx + 1:02X}"
ret += arg
ret += CHOICE_LIST_END[str_type]
return ret
# Not reached
- raise ValueError("Unexpected string command '{}'".format(self.name))
+ raise ValueError(f"Unexpected string command '{self.name}'")
def get_type(self):
if self.name in commands:
@@ -653,34 +649,34 @@ def __init__(self, string, lang, pos):
if end < len(string) and string[end] == "=":
command_name += "="
if command_name not in commands and command_name not in special_commands:
- raise generic.ScriptError('Undefined command "{}"'.format(command_name), pos)
+ raise generic.ScriptError(f'Undefined command "{command_name}"', pos)
if command_name in commands and "deprecated" in commands[command_name]:
generic.print_warning(
generic.Warning.DEPRECATION,
- "String code '{}' has been deprecated and will be removed soon".format(command_name),
+ f"String code '{command_name}' has been deprecated and will be removed soon",
pos,
)
del commands[command_name]["deprecated"]
#
command = StringCommand(command_name, cmd_pos, pos)
if end >= len(string):
- raise generic.ScriptError("Missing '}}' from command \"{}\"".format(string[start:]), pos)
+ raise generic.ScriptError(f"Missing '}}' from command \"{string[start:]}\"", pos)
if string[end] == ".":
if command_name not in commands or "allow_case" not in commands[command_name]:
- raise generic.ScriptError('Command "{}" can\'t have a case'.format(command_name), pos)
+ raise generic.ScriptError(f'Command "{command_name}" can\'t have a case', pos)
case_start = end + 1
end = case_start
while end < len(string) and string[end] not in "} ":
end += 1
case = string[case_start:end]
if lang.cases is None or case not in lang.cases:
- raise generic.ScriptError('Invalid case-name "{}"'.format(case), pos)
+ raise generic.ScriptError(f'Invalid case-name "{case}"', pos)
command.case = lang.cases[case]
if string[end] != "}":
arg_start = end + 1
end = string.find("}", end + 1)
if end == -1 or not command.set_arguments(string[arg_start:end]):
- raise generic.ScriptError("Missing '}}' from command \"{}\"".format(string[start:]), pos)
+ raise generic.ScriptError(f"Missing '}}' from command \"{string[start:]}\"", pos)
command.validate_arguments(lang)
if command_name == "G=" and self.components:
raise generic.ScriptError("Set-gender command {G=} must be at the start of the string", pos)
@@ -694,7 +690,7 @@ def __init__(self, string, lang, pos):
):
self.gender = self.components[0].arguments[0]
if self.gender not in lang.genders:
- raise generic.ScriptError("Invalid gender name '{}'".format(self.gender), pos)
+ raise generic.ScriptError(f"Invalid gender name '{self.gender}'", pos)
self.components.pop(0)
else:
self.gender = None
@@ -729,9 +725,8 @@ def remove_non_default_commands(self):
i = 0
while i < len(self.components):
comp = self.components[i]
- if isinstance(comp, StringCommand):
- if comp.name == "P" or comp.name == "G":
- self.components[i] = comp.arguments[-1] if comp.arguments else ""
+ if isinstance(comp, StringCommand) and comp.name in ("P", "G"):
+ self.components[i] = comp.arguments[-1] if comp.arguments else ""
i += 1
def parse_string(self, str_type, lang, wanted_lang_id, static_args):
@@ -766,7 +761,7 @@ def get_command_sizes(self):
sizes_list = []
for idx in range(len(sizes)):
if idx not in sizes:
- raise generic.ScriptError("String argument {:d} is not used".format(idx), self.pos)
+ raise generic.ScriptError(f"String argument {idx} is not used", self.pos)
sizes_list.append(sizes[idx])
return sizes_list
@@ -992,16 +987,14 @@ def get_string(self, string, lang_id):
str_type = self.strings[string_id].get_type()
parsed_string = ""
if self.strings[string_id].gender is not None:
- parsed_string += SET_STRING_GENDER[str_type] + "\\{:02X}".format(
- self.genders[self.strings[string_id].gender]
- )
+ parsed_string += SET_STRING_GENDER[str_type] + f"\\{self.genders[self.strings[string_id].gender]:02X}"
if len(self.strings[string_id].cases) > 0:
parsed_string += BEGIN_CASE_CHOICE_LIST[str_type]
for case_name, case_string in sorted(self.strings[string_id].cases.items()):
case_id = self.cases[case_name]
parsed_string += (
CHOICE_LIST_ITEM[str_type]
- + "\\{:02X}".format(case_id)
+ + f"\\{case_id:02X}"
+ case_string.parse_string(str_type, self, lang_id, string.params)
)
parsed_string += CHOICE_LIST_DEFAULT[str_type]
@@ -1023,7 +1016,7 @@ def handle_grflangid(self, data, pos):
try:
value = int(lang_text, 16)
except ValueError:
- raise generic.ScriptError("Invalid grflangid {!r}".format(lang_text), pos)
+ raise generic.ScriptError(f"Invalid grflangid {lang_text!r}", pos)
if value < 0 or value >= 0x7F:
raise generic.ScriptError("Invalid grflangid", pos)
self.langid = value
@@ -1073,7 +1066,7 @@ def handle_map_gender(self, data, pos):
if len(genders) != 2:
raise generic.ScriptError("Invalid ##map_gender line", pos)
if genders[0] not in self.genders:
- raise generic.ScriptError("Trying to map non-existing gender '{}'".format(genders[0]), pos)
+ raise generic.ScriptError(f"Trying to map non-existing gender '{genders[0]}'", pos)
self.gender_map[genders[0]].append(genders[1])
def handle_case(self, data, pos):
@@ -1103,7 +1096,7 @@ def handle_map_case(self, data, pos):
if len(cases) != 2:
raise generic.ScriptError("Invalid ##map_case line", pos)
if cases[0] not in self.cases:
- raise generic.ScriptError("Trying to map non-existing case '{}'".format(cases[0]), pos)
+ raise generic.ScriptError(f"Trying to map non-existing case '{cases[0]}'", pos)
self.case_map[cases[0]].append(cases[1])
def handle_text(self, string, case, value, pos):
@@ -1119,11 +1112,11 @@ def handle_text(self, string, case, value, pos):
@param value: Value of the string.
@type value: C{str}
"""
- if not re.match("[A-Za-z_0-9]+$", string):
- raise generic.ScriptError('Invalid string name "{}"'.format(string), pos)
+ if not re.match(r"[A-Za-z_0-9]+$", string):
+ raise generic.ScriptError(f'Invalid string name "{string}"', pos)
if string in self.strings and case is None:
- raise generic.ScriptError('String name "{}" is used multiple times'.format(string), pos)
+ raise generic.ScriptError(f'String name "{string}" is used multiple times', pos)
if self.default:
self.strings[string] = NewGRFString(value, self, pos)
@@ -1131,7 +1124,7 @@ def handle_text(self, string, case, value, pos):
else:
if string not in default_lang.strings:
generic.print_warning(
- generic.Warning.GENERIC, 'String name "{}" does not exist in master file'.format(string), pos
+ generic.Warning.GENERIC, f'String name "{string}" does not exist in master file', pos
)
return
@@ -1139,7 +1132,7 @@ def handle_text(self, string, case, value, pos):
if not default_lang.strings[string].match_commands(newgrf_string):
generic.print_warning(
generic.Warning.GENERIC,
- 'String commands don\'t match with master file "{}"'.format(DEFAULT_LANGNAME),
+ f'String commands don\'t match with master file "{DEFAULT_LANGNAME}"',
pos,
)
return
@@ -1151,10 +1144,10 @@ def handle_text(self, string, case, value, pos):
generic.print_warning(generic.Warning.GENERIC, "String with case used before the base string", pos)
return
if self.cases is None or case not in self.cases:
- generic.print_warning(generic.Warning.GENERIC, 'Invalid case name "{}"'.format(case), pos)
+ generic.print_warning(generic.Warning.GENERIC, f'Invalid case name "{case}"', pos)
return
if case in self.strings[string].cases:
- raise generic.ScriptError('String name "{}.{}" is used multiple times'.format(string, case), pos)
+ raise generic.ScriptError(f'String name "{string}.{case}" is used multiple times', pos)
if newgrf_string.gender:
generic.print_warning(
generic.Warning.GENERIC, "Case-strings can't set the gender, only the base string can", pos
@@ -1167,7 +1160,7 @@ def handle_string(self, line, pos):
# Silently ignore empty lines.
return
- elif len(line) > 2 and line[:2] == "##" and not line[:3] == "###":
+ elif len(line) > 2 and line[:2] == "##" and line[:3] != "###":
# "##pragma" line, call relevant handler.
if self.default:
# Default language ignores all pragmas.
@@ -1230,7 +1223,7 @@ def parse_file(filename, default):
"""
lang = Language(False)
try:
- with open(generic.find_file(filename), "r", encoding="utf-8") as fh:
+ with open(generic.find_file(filename), encoding="utf-8") as fh:
for idx, line in enumerate(fh):
pos = generic.LinePosition(filename, idx + 1)
line = line.rstrip("\n\r").lstrip("\ufeff")
@@ -1260,9 +1253,7 @@ def parse_file(filename, default):
else:
for lng in langs:
if lng[0] == lang.langid:
- msg = "Language file has the same ##grflangid (with number {:d}) as another language file".format(
- lang.langid
- )
+ msg = f"Language file has the same ##grflangid (with number {lang.langid}) as another language file"
raise generic.ScriptError(msg, generic.LanguageFilePosition(filename))
langs.append((lang.langid, lang))
@@ -1286,7 +1277,7 @@ def read_lang_files(lang_dir, default_lang_file):
if not os.path.exists(lang_dir + os.sep + default_lang_file):
generic.print_warning(
generic.Warning.GENERIC,
- 'Default language file "{}" doesn\'t exist'.format(os.path.join(lang_dir, default_lang_file)),
+ f'Default language file "{os.path.join(lang_dir, default_lang_file)}" doesn\'t exist',
)
return
parse_file(lang_dir + os.sep + default_lang_file, True)
@@ -1301,4 +1292,4 @@ def list_unused_strings():
for string in default_lang.strings:
if string in Language.used_strings:
continue
- generic.print_warning(generic.Warning.GENERIC, 'String "{}" is unused'.format(string))
+ generic.print_warning(generic.Warning.GENERIC, f'String "{string}" is unused')
diff --git a/nml/main.py b/nml/main.py
index afd2435b2..800e8b3b9 100644
--- a/nml/main.py
+++ b/nml/main.py
@@ -68,7 +68,7 @@ def parse_cli(argv):
@return: Options, and input filename (if not supplied, use C{sys.stdin}).
@rtype: C{tuple} (C{Object}, C{str} or C{None})
"""
- usage = "Usage: %prog [options] \n" "Where is the nml file to parse"
+ usage = "Usage: %prog [options] \nWhere is the nml file to parse"
opt_parser = optparse.OptionParser(usage=usage, version=version_info.get_cli_version())
opt_parser.set_defaults(
@@ -194,9 +194,7 @@ def parse_cli(argv):
type="int",
dest="verbosity",
metavar="",
- help="Set the verbosity level for informational output. [default: %default, max: {}]".format(
- generic.VERBOSITY_MAX
- ),
+ help=f"Set the verbosity level for informational output. [default: %default, max: {generic.VERBOSITY_MAX}]",
)
opt_parser.add_option(
"-D", "--debug-parser", action="store_true", dest="debug_parser", help="Enable debug mode for parser."
@@ -252,7 +250,7 @@ def parse_cli(argv):
else:
input_filename = args[0]
if not os.access(input_filename, os.R_OK):
- raise generic.ScriptError('Input file "{}" does not exist'.format(input_filename))
+ raise generic.ScriptError(f'Input file "{input_filename}" does not exist')
return opts, input_filename
@@ -307,7 +305,7 @@ def main(argv):
if input_filename is None:
input = sys.stdin
else:
- input = codecs.open(generic.find_file(input_filename), "r", "utf-8")
+ input = codecs.open(generic.find_file(input_filename), "r", "utf-8") # noqa: SIM115 # explicit handle usage
# Only append an output grf name, if no output is given, also not implicitly via -M
if not opts.outputfile_given and not outputs:
opts.grf_filename = filename_output_from_input(input_filename, ".grf")
@@ -337,7 +335,7 @@ def main(argv):
elif outext == ".dep":
outputs.append(output_dep.OutputDEP(output, opts.grf_filename))
else:
- generic.print_error("Unknown output format {}".format(outext))
+ generic.print_error(f"Unknown output format {outext}")
sys.exit(2)
ret = nml(
@@ -413,7 +411,7 @@ def nml(
try:
script = inputfile.read()
except UnicodeDecodeError as ex:
- raise generic.ScriptError("Input file is not utf-8 encoded: {}".format(ex))
+ raise generic.ScriptError(f"Input file is not utf-8 encoded: {ex}")
# Strip a possible BOM
script = script.lstrip(str(codecs.BOM_UTF8, "utf-8"))
@@ -539,7 +537,7 @@ def nml(
if im.mode != "P":
continue
pal = palette.validate_palette(im, f)
- except IOError as ex:
+ except OSError as ex:
raise generic.ImageError(str(ex), f)
if (
@@ -547,9 +545,7 @@ def nml(
and pal != forced_palette
and not (forced_palette == "DEFAULT" and pal == "LEGACY")
):
- raise generic.ImageError(
- "Image has '{}' palette, but you forced the '{}' palette".format(pal, used_palette), f
- )
+ raise generic.ImageError(f"Image has '{pal}' palette, but you forced the '{used_palette}' palette", f)
if used_palette == "ANY":
used_palette = pal
@@ -558,7 +554,7 @@ def nml(
used_palette = "DEFAULT"
else:
raise generic.ImageError(
- "Image has '{}' palette, but \"{}\" has the '{}' palette".format(pal, last_file, used_palette),
+ f"Image has '{pal}' palette, but \"{last_file}\" has the '{used_palette}' palette",
f,
)
last_file = f
@@ -661,7 +657,7 @@ def run():
# User mode: print user friendly error message.
ex_msg = str(ex)
if len(ex_msg) > 0:
- ex_msg = '"{}"'.format(ex_msg)
+ ex_msg = f'"{ex_msg}"'
traceback = sys.exc_info()[2]
# Walk through the traceback object until we get to the point where the exception happened.
@@ -680,7 +676,7 @@ def run():
"version": version,
"msg": ex_msg,
"cli": sys.argv,
- "loc": 'File "{}", line {:d}, in {}'.format(filename, lineno, name),
+ "loc": f'File "{filename}", line {lineno}, in {name}',
}
msg = (
diff --git a/nml/nmlop.py b/nml/nmlop.py
index 7d9a9f4f6..39ebf4c7e 100644
--- a/nml/nmlop.py
+++ b/nml/nmlop.py
@@ -98,9 +98,9 @@ def to_string(self, expr1, expr2):
@rtype: C{str}
"""
if self.prefix_text is not None:
- return "{}({}, {})".format(self.prefix_text, expr1, expr2)
+ return f"{self.prefix_text}({expr1}, {expr2})"
else: # Infix notation.
- return "({} {} {})".format(expr1, self.token, expr2)
+ return f"({expr1} {self.token} {expr2})"
def __call__(self, expr1, expr2, pos=None):
return binop.BinOp(self, expr1, expr2, pos)
diff --git a/nml/output_base.py b/nml/output_base.py
index 8f1cf2b63..bc7637755 100644
--- a/nml/output_base.py
+++ b/nml/output_base.py
@@ -1,3 +1,7 @@
+"""
+Abstract base classes that implements common functionality for output classes
+"""
+
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,9 +17,6 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-"""
-Abstract base classes that implements common functionality for output classes
-"""
import array
import io
@@ -47,7 +48,7 @@ def open(self):
"""
Open the output file. Data gets stored in-memory.
"""
- raise NotImplementedError("Implement me in {}".format(type(self)))
+ raise NotImplementedError(f"Implement me in {type(self)}")
def open_file(self):
"""
@@ -56,7 +57,7 @@ def open_file(self):
@return: File handle of the opened file.
@rtype: C{file}
"""
- raise NotImplementedError("Implement me in {}".format(type(self)))
+ raise NotImplementedError(f"Implement me in {type(self)}")
def assemble_file(self, real_file):
"""
@@ -214,7 +215,7 @@ def print_bytex(self, byte, pretty_print=None):
@param byte: Value to output.
@type byte: C{int}
"""
- raise NotImplementedError("Implement print_bytex() in {}".format(type(self)))
+ raise NotImplementedError(f"Implement print_bytex() in {type(self)}")
def print_wordx(self, byte):
"""
@@ -223,7 +224,7 @@ def print_wordx(self, byte):
@param byte: Value to output.
@type byte: C{int}
"""
- raise NotImplementedError("Implement print_wordx() in {}".format(type(self)))
+ raise NotImplementedError(f"Implement print_wordx() in {type(self)}")
def print_dwordx(self, byte):
"""
@@ -232,7 +233,7 @@ def print_dwordx(self, byte):
@param byte: Value to output.
@type byte: C{int}
"""
- raise NotImplementedError("Implement print_dwordx() in {}".format(type(self)))
+ raise NotImplementedError(f"Implement print_dwordx() in {type(self)}")
def newline(self, msg="", prefix="\t"):
"""
@@ -245,7 +246,7 @@ def newline(self, msg="", prefix="\t"):
@param prefix: Additional white space in front of the comment.
@type prefix: C{str}
"""
- raise NotImplementedError("Implement newline() in {}".format(type(self)))
+ raise NotImplementedError(f"Implement newline() in {type(self)}")
def comment(self, msg):
"""
@@ -256,7 +257,7 @@ def comment(self, msg):
@note: Only use if no bytes have been written to the current line.
"""
- raise NotImplementedError("Implement comment() in {}".format(type(self)))
+ raise NotImplementedError(f"Implement comment() in {type(self)}")
def start_sprite(self, expected_size, is_real_sprite=False):
"""
@@ -283,8 +284,8 @@ def end_sprite(self):
assert self.in_sprite
self.in_sprite = False
self.newline()
- assert self.expected_count == self.byte_count, "Expected {:d} bytes to be written to sprite, got {:d}".format(
- self.expected_count, self.byte_count
+ assert self.expected_count == self.byte_count, (
+ f"Expected {self.expected_count} bytes to be written to sprite, got {self.byte_count}"
)
diff --git a/nml/output_grf.py b/nml/output_grf.py
index 2312f3255..2595a9fcd 100644
--- a/nml/output_grf.py
+++ b/nml/output_grf.py
@@ -13,6 +13,7 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
+import contextlib
import hashlib
import os
@@ -35,11 +36,8 @@ def open_file(self):
# Remove / unlink the file, most useful for linux systems
# See also issue #4165
# If the file happens to be in use or non-existent, ignore
- try:
+ with contextlib.suppress(OSError):
os.unlink(self.filename)
- except OSError:
- # Ignore
- pass
return open(self.filename, "wb")
def get_md5(self):
diff --git a/nml/output_nfo.py b/nml/output_nfo.py
index bcb8ba786..ae136d5c7 100644
--- a/nml/output_nfo.py
+++ b/nml/output_nfo.py
@@ -43,7 +43,7 @@ def open(self):
self.file = io.StringIO()
def open_file(self):
- handle = open(self.filename, "w", encoding="utf-8")
+ handle = open(self.filename, "w", encoding="utf-8") # noqa: SIM115 # explicit handle usage
handle.write(
"// Automatically generated by GRFCODEC. Do not modify!\n"
"// (Info version 32)\n"
@@ -68,23 +68,23 @@ def print_bytex(self, value, pretty_print=None):
if pretty_print is not None:
self.file.write(pretty_print + " ")
return
- self.file.write("{:02X} ".format(value))
+ self.file.write(f"{value:02X} ")
def print_word(self, value):
value = self.prepare_word(value)
- self.file.write("\\w{:d} ".format(value))
+ self.file.write(f"\\w{value:d} ")
def print_wordx(self, value):
value = self.prepare_word(value)
- self.file.write("\\wx{:04X} ".format(value))
+ self.file.write(f"\\wx{value:04X} ")
def print_dword(self, value):
value = self.prepare_dword(value)
- self.file.write("\\d{:d} ".format(value))
+ self.file.write(f"\\d{value:d} ")
def print_dwordx(self, value):
value = self.prepare_dword(value)
- self.file.write("\\dx{:08X} ".format(value))
+ self.file.write(f"\\dx{value:08X} ")
def print_string(self, value, final_zero=True, force_ascii=False):
assert self.in_sprite
diff --git a/nml/parser.py b/nml/parser.py
index efabf0ec6..d654c997e 100644
--- a/nml/parser.py
+++ b/nml/parser.py
@@ -13,8 +13,6 @@
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""
-from nml.ply import yacc
-
from nml import expression, generic, nmlop, tokens, unit
from nml.actions import actionD, real_sprite
from nml.ast import (
@@ -45,6 +43,7 @@
townnames,
tracktypetable,
)
+from nml.ply import yacc
class NMLParser:
@@ -93,7 +92,7 @@ def p_error(self, t):
if t is None:
raise generic.ScriptError("Syntax error, unexpected end-of-file")
else:
- raise generic.ScriptError('Syntax error, unexpected token "{}"'.format(t.value), t.lineno)
+ raise generic.ScriptError(f'Syntax error, unexpected token "{t.value}"', t.lineno)
#
# Main script blocks
diff --git a/nml/spritecache.py b/nml/spritecache.py
index b65d3ba87..a16e43a8c 100644
--- a/nml/spritecache.py
+++ b/nml/spritecache.py
@@ -156,7 +156,7 @@ def read_cache(self):
except json.JSONDecodeError:
generic.print_warning(
generic.Warning.GENERIC,
- "{} contains invalid data, ignoring.".format(index_file_name)
+ f"{index_file_name} contains invalid data, ignoring."
+ " Please remove the file and file a bug report if this warning keeps appearing",
)
self.cached_sprites = {}
@@ -252,7 +252,7 @@ def read_cache(self):
except Exception:
generic.print_warning(
generic.Warning.GENERIC,
- "{} contains invalid data, ignoring.".format(index_file_name)
+ f"{index_file_name} contains invalid data, ignoring."
+ " Please remove the file and file a bug report if this warning keeps appearing",
)
self.cached_sprites = {} # Clear cache
@@ -312,9 +312,10 @@ def write_cache(self):
index_output = json.JSONEncoder(sort_keys=True).encode(index_data)
try:
- with generic.open_cache_file(self.sources, ".cache", "wb") as cache_file, generic.open_cache_file(
- self.sources, ".cacheindex", "w"
- ) as index_file:
+ with (
+ generic.open_cache_file(self.sources, ".cache", "wb") as cache_file,
+ generic.open_cache_file(self.sources, ".cacheindex", "w") as index_file,
+ ):
index_file.write(index_output)
sprite_data.tofile(cache_file)
except OSError:
diff --git a/nml/spriteencoder.py b/nml/spriteencoder.py
index 71a488141..28558c41d 100644
--- a/nml/spriteencoder.py
+++ b/nml/spriteencoder.py
@@ -18,12 +18,6 @@
from nml import generic, lz77, palette, spritecache
from nml.actions import real_sprite
-try:
- from PIL import Image
-except ImportError:
- # Image is required only when using graphics
- pass
-
# Some constants for the 'info' byte
INFO_RGB = 1
INFO_ALPHA = 2
@@ -116,9 +110,7 @@ def open(self, sprite_files):
for sprite_info in sprite_list:
count_sprites += 1
- generic.print_progress(
- "Encoding {}/{}: {}".format(count_sprites, num_sprites, source_name), incremental=True
- )
+ generic.print_progress(f"Encoding {count_sprites}/{num_sprites}: {source_name}", incremental=True)
cache_key = sprite_info.get_cache_key(self.crop_sprites)
cache_item = local_cache.get_item(cache_key, self.palette)
@@ -208,9 +200,8 @@ def get(self, sprite_info):
alpha = pixel_stats.get("alpha", 0)
if alpha > 0 and (sprite_info.flags.value & real_sprite.FLAG_NOALPHA) != 0:
warnings.append(
- "{}: {:d} of {:d} pixels ({:d}%) are semi-transparent, but NOALPHA is in flags".format(
- str(image_32_pos), alpha, total, alpha * 100 // total
- )
+ f"{image_32_pos}: {alpha} of {total} pixels ({alpha * 100 // total}%)"
+ " are semi-transparent, but NOALPHA is in flags"
)
if cache_key[2] is not None:
@@ -219,15 +210,13 @@ def get(self, sprite_info):
anim = pixel_stats.get("anim", 0)
if white > 0 and (sprite_info.flags.value & real_sprite.FLAG_WHITE) == 0:
warnings.append(
- "{}: {:d} of {:d} pixels ({:d}%) are pure white, but WHITE isn't in flags".format(
- str(image_8_pos), white, total, white * 100 // total
- )
+ f"{image_8_pos}: {white} of {total} pixels ({white * 100 // total}%)"
+ " are pure white, but WHITE isn't in flags"
)
if anim > 0 and (sprite_info.flags.value & real_sprite.FLAG_ANIM) == 0:
warnings.append(
- "{}: {:d} of {:d} pixels ({:d}%) are animated, but ANIM isn't in flags".format(
- str(image_8_pos), anim, total, anim * 100 // total
- )
+ f"{image_8_pos}: {anim} of {total} pixels ({anim * 100 // total}%)"
+ " are animated, but ANIM isn't in flags"
)
return (size_x, size_y, xoffset, yoffset, compressed_data, info_byte, crop_rect, warnings)
@@ -242,6 +231,9 @@ def open_image_file(self, filename):
@return: Image file
@rtype: L{Image}
"""
+ # Image is required only when using graphics, existence already checked by entrypoint
+ from PIL import Image
+
if filename in self.cached_image_files:
im = self.cached_image_files[filename]
else:
@@ -300,12 +292,12 @@ def encode_sprite(self, sprite_info):
(im_width, im_height) = im.size
if x < 0 or y < 0 or x + size_x > im_width or y + size_y > im_height:
pos = generic.build_position(sprite_info.poslist)
- raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_32bpp.value), pos)
+ raise generic.ScriptError(f"Read beyond bounds of image file '{filename_32bpp.value}'", pos)
try:
sprite = im.crop((x, y, x + size_x, y + size_y))
except OSError:
pos = generic.build_position(sprite_info.poslist)
- raise generic.ImageError("Failed to crop 32bpp {} image".format(im.format), filename_32bpp.value, pos)
+ raise generic.ImageError(f"Failed to crop 32bpp {im.format} image", filename_32bpp.value, pos)
rgb_sprite_data = sprite.tobytes()
if (info_byte & INFO_ALPHA) != 0:
@@ -323,14 +315,12 @@ def encode_sprite(self, sprite_info):
(im_width, im_height) = mask_im.size
if mask_x < 0 or mask_y < 0 or mask_x + size_x > im_width or mask_y + size_y > im_height:
pos = generic.build_position(sprite_info.poslist)
- raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_8bpp.value), pos)
+ raise generic.ScriptError(f"Read beyond bounds of image file '{filename_8bpp.value}'", pos)
try:
mask_sprite = mask_im.crop((mask_x, mask_y, mask_x + size_x, mask_y + size_y))
except OSError:
pos = generic.build_position(sprite_info.poslist)
- raise generic.ImageError(
- "Failed to crop 8bpp {} image".format(mask_im.format), filename_8bpp.value, pos
- )
+ raise generic.ImageError(f"Failed to crop 8bpp {mask_im.format} image", filename_8bpp.value, pos)
mask_sprite_data = self.palconvert(mask_sprite.tobytes(), im_mask_pal)
# Check for white pixels; those that cause "artefacts" when shading
diff --git a/nml/tokens.py b/nml/tokens.py
index 42e0b0c5e..00481e7f3 100644
--- a/nml/tokens.py
+++ b/nml/tokens.py
@@ -16,9 +16,8 @@
import re
import sys
-from nml.ply import lex
-
from nml import expression, generic
+from nml.ply import lex
# fmt: off
reserved = {
@@ -248,13 +247,11 @@ def t_newline(self, t):
self.increment_lines(len(t.value))
def t_error(self, t):
- print(
- (
- "Illegal character '{}' (character code 0x{:02X}) at {}, column {:d}".format(
- t.value[0], ord(t.value[0]), t.lexer.lineno, self.find_column(t)
- )
- )
- )
+ line = t.lexer.lineno
+ col = self.find_column(t)
+ char = t.value[0]
+ hex_ord = f"0x{ord(char):02X}"
+ print(f"Illegal character '{char}' (character code {hex_ord}) at {line}, column {col}")
sys.exit(1)
def build(self):
diff --git a/nml/version_info.py b/nml/version_info.py
index b9e9bc69c..17034a9a5 100644
--- a/nml/version_info.py
+++ b/nml/version_info.py
@@ -64,17 +64,17 @@ def get_cli_version():
result = get_nml_version() + "\n\n"
nmlc_path = os.path.abspath(sys.argv[0])
- result += "nmlc: {}\n".format(nmlc_path)
+ result += f"nmlc: {nmlc_path}\n"
from nml import lz77
lz77_ver = "C (native)" if lz77.is_native else "Python"
- result += "LZ77 implementation: {}\n\n".format(lz77_ver)
+ result += f"LZ77 implementation: {lz77_ver}\n\n"
result += "Library versions encountered:\n"
for lib, lib_ver in get_lib_versions().items():
- result += " {}: {}\n".format(lib, lib_ver)
+ result += f" {lib}: {lib_ver}\n"
- result += "\nPython: {}\n".format(sys.executable)
- result += "version {}".format(sys.version)
+ result += f"\nPython: {sys.executable}\n"
+ result += f"version {sys.version}"
return result
diff --git a/nml/version_update.py b/nml/version_update.py
index 5599e5a33..950aa4a82 100644
--- a/nml/version_update.py
+++ b/nml/version_update.py
@@ -49,7 +49,7 @@ def get_git_version():
release = describe[1] == "0"
changeset = describe[2]
except OSError as e:
- print("Git checkout found but cannot determine its version. Error({0}): {1}".format(e.errno, e.strerror))
+ print(f"Git checkout found but cannot determine its version. Error({e.errno}): {e.strerror}")
return None
except subprocess.CalledProcessError as e:
print("Git checkout found but cannot determine its version. Error: ", e)
@@ -93,8 +93,8 @@ def get_and_write_version():
path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
with open(os.path.join(path, "nml", "__version__.py"), "w") as file:
file.write("# this file is autogenerated by setup.py\n")
- file.write('version = "{}"\n'.format(version))
+ file.write(f'version = "{version}"\n')
return version.split()[0]
- except IOError:
+ except OSError:
print("Version file NOT written")
return None
diff --git a/pyproject.toml b/pyproject.toml
index 71b7f39d6..18f2bb7e6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,81 @@
+[project]
+name = "nml"
+dynamic = ["version"]
+description = "An OpenTTD NewGRF compiler for the nml language"
+readme = "README.md"
+license = "GPL-2.0+"
+license-files = ["LICENSE"]
+authors = [
+ {name = "NML Development Team", email = "info@openttd.org"},
+]
+classifiers=[
+ "Environment :: Console",
+ "Intended Audience :: Developers",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Topic :: Software Development :: Compilers",
+]
+
+requires-python = ">=3.9"
+dependencies = [
+ "Pillow>=3.4",
+]
+
+[project.urls]
+Repository = "https://github.com/OpenTTD/nml"
+Issues = "https://github.com/OpenTTD/nml/issues"
+Changelog = "https://github.com/OpenTTD/nml/blob/master/docs/changelog.txt"
+
+[project.scripts]
+nmlc = "nml.main:run"
+
[build-system]
-requires = ["setuptools", "ply"]
+requires = ["setuptools"]
+
+[tool.setuptools.package-dir]
+nml = "nml"
[tool.black]
line-length = 120
-exclude = 'action2var_variables.py|action3_callbacks.py|ply|setup.py'
+exclude = 'action2var_variables.py|action3_callbacks.py|ply'
+
+[tool.ruff]
+line-length = 120
+exclude = [
+ "nml/ply/*",
+]
+
+[tool.ruff.lint]
+select = [
+ "E", # pycodestyle
+ "F", # Pyflakes
+ "UP", # pyupgrade
+ "B", # flake8-bugbear
+ "SIM", # flake8-simplify
+ "I", # isort
+]
+
+ignore = [
+ "B904", # use of nested exceptions
+ "SIM108", # rewrite as ternary
+]
+
+# Files with tables of data, not conducive to being linted or formatted
+[tool.ruff.lint.per-file-ignores]
+"nml/palette.py" = ["E203", "E241"]
+"nml/grfstrings.py" = ["E203", "E241"]
+"nml/global_constants.py" = ["E203", "E241"]
+"nml/actions/real_sprite.py" = ["E203", "E241"]
+"nml/actions/action2var_variables.py" = ["E203", "E241", "E501"]
+"nml/actions/action3_callbacks.py" = ["E203", "E241", "E501"]
+
+[tool.ruff.format]
+exclude = [
+ "nml/actions/action2var_variables.py",
+ "nml/actions/action3_callbacks.py",
+]
diff --git a/setup.py b/setup.py
old mode 100755
new mode 100644
index 3f3d0d11f..f515b54a8
--- a/setup.py
+++ b/setup.py
@@ -1,9 +1,4 @@
-#!/usr/bin/env python3
-
-from setuptools import Distribution, Extension, find_packages, setup
-
-import contextlib
-import os
+from setuptools import Extension, setup
try:
# Update the version by querying git if possible.
@@ -17,42 +12,8 @@
NML_VERSION = version_info.get_nml_version()
-default_dist = Distribution()
-default_build_py = default_dist.get_command_class('build_py')
-default_clean = default_dist.get_command_class('clean')
-
setup(
name="nml",
version=NML_VERSION,
- packages=find_packages(),
- description="An OpenTTD NewGRF compiler for the nml language",
- long_description=(
- "A tool to compile NewGRFs for OpenTTD from nml files"
- "NML is a meta-language that aims to be a lot simpler to"
- " learn and use than nfo used traditionally to write NewGRFs."
- ),
- license="GPL-2.0+",
- classifiers=[
- "Development Status :: 2 - Pre-Alpha",
- "Environment :: Console",
- "Intended Audience :: Developers",
- "License :: OSI Approved :: GNU General Public License (GPL)",
- "Operating System :: OS Independent",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
- "Programming Language :: Python :: 3.12",
- "Programming Language :: Python :: 3.13",
- "Programming Language :: Python :: 3.14",
- "Topic :: Software Development :: Compilers",
- ],
- url="https://github.com/OpenTTD/nml",
- author="NML Development Team",
- author_email="info@openttd.org",
- entry_points={"console_scripts": ["nmlc = nml.main:run"]},
ext_modules=[Extension("nml_lz77", ["nml/_lz77.c"], optional=True)],
- python_requires=">=3.10",
- install_requires=[
- "Pillow>=3.4",
- ],
)