From 21da3b66ffdd2597bf358d4e37eeb826fd251c3d Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 4 Jun 2023 16:32:56 +0100 Subject: [PATCH] Update for devtest-20230531 engine. --- .editorconfig | 818 ++++++++++++++++-- OpenRA.Mods.Mobius/RemasterPackages.cs | 2 +- OpenRA.Mods.Mobius/RemasterSpriteSequence.cs | 312 +++---- OpenRA.Mods.Mobius/Terrain/RemasterTerrain.cs | 8 +- .../Terrain/RemasterTileCache.cs | 34 +- .../Traits/Palettes/ColorPickerColorShift.cs | 32 +- .../Traits/Palettes/FixedColorShift.cs | 10 +- .../Traits/Palettes/FixedPlayerColorShift.cs | 2 +- .../Traits/Palettes/PlayerColorShift.cs | 10 +- .../Traits/World/RemasterTerrainRenderer.cs | 11 +- .../RemasterTilesetConverter.cs | 9 +- mod.config | 2 +- mods/cnc/languages/difficulties/en.ftl | 7 + mods/cnc/languages/lua/en.ftl | 162 ++++ mods/cnc/mod.yaml | 23 +- packaging/windows/buildpackage.nsi | 4 - 16 files changed, 1110 insertions(+), 336 deletions(-) create mode 100644 mods/cnc/languages/difficulties/en.ftl create mode 100644 mods/cnc/languages/lua/en.ftl diff --git a/.editorconfig b/.editorconfig index aa72961..fa7e52c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,24 +8,444 @@ end_of_line = LF insert_final_newline = true trim_trailing_whitespace = true +; 4-column tab indentation +[*.yaml] +indent_style = tab +indent_size = 4 + ; 4-column tab indentation and .NET coding conventions [*.cs] indent_style = tab indent_size = 4 -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true +#### Code Style Rules +#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ + +# Severity Levels: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-options#severity-level +# Below we enable specific rules by setting severity to warning. +# Rules are disabled by setting severity to silent (to still allow use in IDE) or none (to prevent all use). +# Rules are listed below with any options available. +# Options are commented out if they match the defaults. + +### Language Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules + +## this and Me preferences + +# IDE0003/IDE0009 Remove 'this' or 'Me' qualification/Add 'this' or 'Me' qualification +#dotnet_style_qualification_for_field = false +#dotnet_style_qualification_for_property = false +#dotnet_style_qualification_for_method = false +#dotnet_style_qualification_for_event = false +dotnet_diagnostic.IDE0003.severity = warning +dotnet_diagnostic.IDE0009.severity = warning + +## Use languages keywords for types + +# IDE0049 Use language keywords instead of framework type names for type references +#dotnet_style_predefined_type_for_locals_parameters_members = true +#dotnet_style_predefined_type_for_member_access = true +dotnet_diagnostic.IDE0049.severity = warning + +## Modifier preferences + +# IDE0036 Order modifiers +#csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0040 Add accessibility modifiers +dotnet_style_require_accessibility_modifiers = omit_if_default +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0044 Add readonly modifier +#dotnet_style_readonly_field = true +dotnet_diagnostic.IDE0044.severity = warning + +# IDE0062 Make local function static +#csharp_prefer_static_local_function = true +dotnet_diagnostic.IDE0062.severity = warning + +# IDE0064 Make struct fields writable +# No options +dotnet_diagnostic.IDE0064.severity = warning + +## Parentheses preferences + +# IDE0047/IDE0048 Remove unnecessary parentheses/Add parentheses for clarity +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary +#dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +#dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_diagnostic.IDE0047.severity = warning +dotnet_diagnostic.IDE0048.severity = warning + +## Expression-level preferences + +# IDE0010 Add missing cases to switch statement +# No options +dotnet_diagnostic.IDE0010.severity = silent + +# IDE0017 Use object initializers +#dotnet_style_object_initializer = true +dotnet_diagnostic.IDE0017.severity = warning + +# IDE0018 Inline variable declaration +#csharp_style_inlined_variable_declaration = true +dotnet_diagnostic.IDE0018.severity = warning + +# IDE0028 Use collection initializers +#dotnet_style_collection_initializer = true +dotnet_diagnostic.IDE0028.severity = warning + +# IDE0032 Use auto-implemented property +#dotnet_style_prefer_auto_properties = true +dotnet_diagnostic.IDE0032.severity = warning + +# IDE0033 Use explicitly provided tuple name +#dotnet_style_explicit_tuple_names = true +dotnet_diagnostic.IDE0033.severity = warning + +# IDE0034 Simplify 'default' expression +#csharp_prefer_simple_default_expression = true +dotnet_diagnostic.IDE0034.severity = warning + +# IDE0037 Use inferred member name +#dotnet_style_prefer_inferred_tuple_names = true +#dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_diagnostic.IDE0037.severity = silent + +# IDE0039 Use local function instead of lambda +#csharp_style_prefer_local_over_anonymous_function = true +dotnet_diagnostic.IDE0039.severity = warning + +# IDE0042 Deconstruct variable declaration +#csharp_style_deconstructed_variable_declaration = true +dotnet_diagnostic.IDE0042.severity = warning + +# IDE0045 Use conditional expression for assignment +#dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_diagnostic.IDE0045.severity = silent + +# IDE0046 Use conditional expression for return +#dotnet_style_prefer_conditional_expression_over_return = true +dotnet_diagnostic.IDE0046.severity = silent + +# IDE0050 Convert anonymous type to tuple +# No options +dotnet_diagnostic.IDE0050.severity = silent + +# IDE0054/IDE0074 Use compound assignment/Use coalesce compound assignment +#dotnet_style_prefer_compound_assignment = true +dotnet_diagnostic.IDE0054.severity = warning +dotnet_diagnostic.IDE0074.severity = warning + +# IDE0056 Use index operator +#csharp_style_prefer_index_operator = true +dotnet_diagnostic.IDE0056.severity = warning + +# IDE0057 Use range operator +#csharp_style_prefer_range_operator = true +dotnet_diagnostic.IDE0057.severity = warning + +# IDE0070 Use 'System.HashCode.Combine' +# No options +dotnet_diagnostic.IDE0070.severity = warning + +# IDE0071 Simplify interpolation +#dotnet_style_prefer_simplified_interpolation = true +dotnet_diagnostic.IDE0071.severity = warning + +# IDE0072 Add missing cases to switch expression +# No options +dotnet_diagnostic.IDE0072.severity = silent + +# IDE0075 Simplify conditional expression +#dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_diagnostic.IDE0075.severity = warning + +# IDE0082 Convert 'typeof' to 'nameof' +# No options +dotnet_diagnostic.IDE0082.severity = warning + +# IDE0090 Simplify 'new' expression +#csharp_style_implicit_object_creation_when_type_is_apparent = true +dotnet_diagnostic.IDE0090.severity = warning + +# IDE0180 Use tuple to swap values +#csharp_style_prefer_tuple_swap = true +dotnet_diagnostic.IDE0180.severity = warning + +## Namespace declaration preferences + +# IDE0160/IDE0161 Use block-scoped namespace/Use file-scoped namespace +#csharp_style_namespace_declarations = block_scoped +dotnet_diagnostic.IDE0160.severity = warning +dotnet_diagnostic.IDE0161.severity = warning + +## Null-checking preferences + +# IDE0016 Use throw expression +#csharp_style_throw_expression = true +dotnet_diagnostic.IDE0016.severity = silent + +# IDE0029/IDE0030/IDE0270 Use coalesce expression (non-nullable types)/Use coalesce expression (nullable types)/Use coalesce expression (if null) +#dotnet_style_coalesce_expression = true +dotnet_diagnostic.IDE0029.severity = warning +dotnet_diagnostic.IDE0030.severity = warning +dotnet_diagnostic.IDE0270.severity = silent + +# IDE0031 Use null propagation +#dotnet_style_null_propagation = true +dotnet_diagnostic.IDE0031.severity = warning + +# IDE0041 Use 'is null' check +#dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_diagnostic.IDE0041.severity = warning + +# IDE0150 Prefer 'null' check over type check +#csharp_style_prefer_null_check_over_type_check = true +dotnet_diagnostic.IDE0150.severity = warning + +# IDE1005 Use conditional delegate call +csharp_style_conditional_delegate_call = true # true is the default, but the rule is not triggered if this is not specified. +dotnet_diagnostic.IDE1005.severity = warning + +## var preferences + +# IDE0007/IDE0008 Use 'var' instead of explicit type/Use explicit type instead of 'var' +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true +csharp_style_var_elsewhere = true +dotnet_diagnostic.IDE0007.severity = warning +dotnet_diagnostic.IDE0008.severity = warning + +## Expression-bodied-members + +# IDE0021 Use expression body for constructors +#csharp_style_expression_bodied_constructors = false +dotnet_diagnostic.IDE0021.severity = silent + +# IDE0022 Use expression body for methods +#csharp_style_expression_bodied_methods = false +dotnet_diagnostic.IDE0022.severity = silent + +# IDE0023/IDE0024 Use expression body for conversion operators/Use expression body for operators +#csharp_style_expression_bodied_operators = false +dotnet_diagnostic.IDE0023.severity = silent +dotnet_diagnostic.IDE0024.severity = silent + +# IDE0025 Use expression body for properties +#csharp_style_expression_bodied_properties = true +dotnet_diagnostic.IDE0025.severity = silent + +# IDE0026 Use expression body for indexers +#csharp_style_expression_bodied_indexers = true +dotnet_diagnostic.IDE0026.severity = silent + +# IDE0027 Use expression body for accessors +#csharp_style_expression_bodied_accessors = true +dotnet_diagnostic.IDE0027.severity = warning + +# IDE0053 Use expression body for lambdas +# This rule is buggy and not enforced for builds. ':warning' will at least enforce it in the IDE. +csharp_style_expression_bodied_lambdas = when_on_single_line:warning +dotnet_diagnostic.IDE0053.severity = warning + +# IDE0061 Use expression body for local functions +csharp_style_expression_bodied_local_functions = when_on_single_line +dotnet_diagnostic.IDE0061.severity = warning + +## Pattern matching preferences + +# IDE0019 Use pattern matching to avoid 'as' followed by a 'null' check +#csharp_style_pattern_matching_over_as_with_null_check = true +dotnet_diagnostic.IDE0019.severity = warning + +# IDE0020/IDE0038 Use pattern matching to avoid 'is' check followed by a cast (with variable)/Use pattern matching to avoid 'is' check followed by a cast (without variable) +#csharp_style_pattern_matching_over_is_with_cast_check = true +dotnet_diagnostic.IDE0020.severity = warning +dotnet_diagnostic.IDE0038.severity = warning + +# IDE0066 Use switch expression +#csharp_style_prefer_switch_expression = true +dotnet_diagnostic.IDE0066.severity = silent + +# IDE0078 Use pattern matching +#csharp_style_prefer_pattern_matching = true +dotnet_diagnostic.IDE0078.severity = silent + +# IDE0083 Use pattern matching ('not' operator) +#csharp_style_prefer_not_pattern = true +dotnet_diagnostic.IDE0083.severity = warning + +# IDE0170 Simplify property pattern +#csharp_style_prefer_extended_property_pattern = true +dotnet_diagnostic.IDE0170.severity = silent # Requires C# 10 + +## Code block preferences + +# IDE0011 Add braces +#csharp_prefer_braces = true +# No options match the style used in OpenRA. +dotnet_diagnostic.IDE0011.severity = none + +# IDE0063 Use simple 'using' statement +#csharp_prefer_simple_using_statement = true +dotnet_diagnostic.IDE0063.severity = silent + +## 'using' directive preferences + +# IDE0065 'using' directive placement +#csharp_using_directive_placement = outside_namespace +dotnet_diagnostic.IDE0065.severity = silent + +## File header preferences -csharp_style_var_elsewhere = true:suggestion -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion +# IDE0073 Require file header +#file_header_template = unset +# This rule does not allow us to enforce our desired header, as it prefixes the header lines with // comments, meaning we can't apply a region. +dotnet_diagnostic.IDE0073.severity = none -csharp_prefer_braces = when_multiline:suggestion -csharp_using_directive_placement = outside_namespace:suggestion -csharp_new_line_before_open_brace = all -csharp_space_around_binary_operators = before_and_after +## Namespace naming preferences -## Naming styles: +# IDE0130 Namespace does not match folder structure +#dotnet_style_namespace_match_folder = true +# This rule doesn't appear to work (never reports violations) +dotnet_diagnostic.IDE0130.severity = none + +### Unnecessary Code Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules + +# IDE0001 Simplify name +# No options +dotnet_diagnostic.IDE0001.severity = warning + +# IDE0002 Simplify member access +# No options +dotnet_diagnostic.IDE0002.severity = warning + +# IDE0004 Remove unnecessary cast +# No options +dotnet_diagnostic.IDE0004.severity = warning + +# IDE0005 Remove unnecessary import +# No options +# IDE0005 is only enabled in the IDE by default. https://github.com/dotnet/roslyn/issues/41640 +# To enable it for builds outside the IDE the 'GenerateDocumentationFile' property must be enabled on the build. +# GenerateDocumentationFile generates additional warnings about XML docs, so disable any we don't care about. +dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0035 Remove unreachable code +# No options +# Duplicates compiler warning CS0162 +dotnet_diagnostic.IDE0035.severity = none + +# IDE0051 Remove unused private member +# No options +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0052 Remove unread private member +# No options +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0058 Remove unnecessary expression value +#csharp_style_unused_value_expression_statement_preference = discard_variable +dotnet_diagnostic.IDE0058.severity = silent + +# IDE0059 Remove unnecessary value assignment +#csharp_style_unused_value_assignment_preference = discard_variable +dotnet_diagnostic.IDE0059.severity = warning + +# IDE0060 Remove unused parameter +dotnet_code_quality_unused_parameters = non_public +dotnet_diagnostic.IDE0060.severity = warning + +# IDE0079 Remove unnecessary suppression +#dotnet_remove_unnecessary_suppression_exclusions = none +dotnet_diagnostic.IDE0079.severity = warning + +# IDE0080 Remove unnecessary suppression operator +# No options +dotnet_diagnostic.IDE0080.severity = warning + +# IDE0100 Remove unnecessary equality operator +# No options +dotnet_diagnostic.IDE0100.severity = warning + +# IDE0110 Remove unnecessary discard +# No options +dotnet_diagnostic.IDE0110.severity = warning + +### Miscellaneous Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/miscellaneous-rules + +# IDE0076 Remove invalid global 'SuppressMessageAttribute' +# No options +dotnet_diagnostic.IDE0076.severity = warning + +# IDE0077 Avoid legacy format target in global 'SuppressMessageAttribute' +# No options +dotnet_diagnostic.IDE0077.severity = warning + +### Formatting Rules (IDE0055) +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055 + +# We may eventually wish to enforce this rule, however some existing formatting conflicts with the rule despite being reasonable. +# Additionally, the rule is buggy and likes to report spuriously after invoking Format Document in the IDE. +dotnet_diagnostic.IDE0055.severity = none + +#dotnet_sort_system_directives_first = true +#dotnet_separate_import_directive_groups = false +#dotnet_style_namespace_match_folder = true + +#csharp_new_line_before_open_brace = all +#csharp_new_line_before_else = true +#csharp_new_line_before_catch = true +#csharp_new_line_before_finally = true +#csharp_new_line_before_members_in_object_initializers = true +#csharp_new_line_before_members_in_anonymous_types = true +#csharp_new_line_between_query_expression_clauses = true + +#csharp_indent_case_contents = true +#csharp_indent_switch_labels = true +#csharp_indent_labels = one_less_than_current +#csharp_indent_block_contents = true +#csharp_indent_braces = false +#csharp_indent_case_contents_when_block = true + +#csharp_space_after_cast = false +#csharp_space_after_keywords_in_control_flow_statements = true +#csharp_space_between_parentheses = +#csharp_space_before_colon_in_inheritance_clause = true +#csharp_space_after_colon_in_inheritance_clause = true +#csharp_space_around_binary_operators = before_and_after +#csharp_space_between_method_declaration_parameter_list_parentheses = false +#csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#csharp_space_between_method_declaration_name_and_open_parenthesis = false +#csharp_space_between_method_call_parameter_list_parentheses = false +#csharp_space_between_method_call_empty_parameter_list_parentheses = false +#csharp_space_between_method_call_name_and_opening_parenthesis = false +#csharp_space_after_comma = true +#csharp_space_before_comma = false +#csharp_space_after_dot = false +#csharp_space_before_dot = false +#csharp_space_after_semicolon_in_for_statement = true +#csharp_space_before_semicolon_in_for_statement = false +#csharp_space_around_declaration_statements = false +#csharp_space_before_open_square_brackets = false +#csharp_space_between_empty_square_brackets = false +#csharp_space_between_square_brackets = false + +#csharp_preserve_single_line_statements = true +#csharp_preserve_single_line_blocks = true + + +### Naming Rules (IDE1006) +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/naming-rules +dotnet_diagnostic.IDE1006.severity = warning + +## Naming styles dotnet_naming_style.camel_case.capitalization = camel_case @@ -34,7 +454,7 @@ dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.i_prefix_pascal_case.capitalization = pascal_case dotnet_naming_style.i_prefix_pascal_case.required_prefix = I -## Symbol specifications: +## Naming Symbols dotnet_naming_symbols.const_locals.applicable_kinds = local dotnet_naming_symbols.const_locals.applicable_accessibilities = * @@ -64,7 +484,7 @@ dotnet_naming_symbols.parameters_and_locals.applicable_accessibilities = * dotnet_naming_symbols.most_symbols.applicable_kinds = namespace, class, struct, enum, field, property, method, local_function, event, delegate, type_parameter dotnet_naming_symbols.most_symbols.applicable_accessibilities = * -## Naming rules: +## Naming Rules dotnet_naming_rule.const_locals_should_be_pascal_case.symbols = const_locals dotnet_naming_rule.const_locals_should_be_pascal_case.style = pascal_case @@ -98,89 +518,355 @@ dotnet_naming_rule.most_symbols_should_be_pascal_case.symbols = most_symbols dotnet_naming_rule.most_symbols_should_be_pascal_case.style = pascal_case dotnet_naming_rule.most_symbols_should_be_pascal_case.severity = warning -## Formatting: -# Also handled by StyleCopAnalyzers - SA1024: ColonsMustBeSpacedCorrectly. -csharp_space_after_colon_in_inheritance_clause = true +### StyleCop.Analyzers +### https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md + +# Below we enable rule categories by setting severity to warning. +# We'll only list rules to disable. +# Individual rules we wish to disable are typically set to none severity. + +# Covers SAxxxx and SXxxxx rules +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.NamingRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.ReadabilityRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = warning +dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpecialRules.severity = warning + +# Rules that are covered by IDE0001 Simplify name +dotnet_diagnostic.SA1125.severity = none # UseShorthandForNullableTypes + +# Rules that are covered by IDE0047 Remove unnecessary parentheses +dotnet_diagnostic.SA1119.severity = none # StatementMustNotUseUnnecessaryParenthesis + +# Rules that are covered by IDE0055 Formatting Rules +dotnet_diagnostic.SA1027.severity = none # UseTabsCorrectly + +# Rules that are covered by IDE1006 Naming Rules +dotnet_diagnostic.SA1300.severity = none # ElementMustBeginWithUpperCaseLetter +dotnet_diagnostic.SA1302.severity = none # InterfaceNamesMustBeginWithI +dotnet_diagnostic.SA1303.severity = none # ConstFieldNamesMustBeginWithUpperCaseLetter +dotnet_diagnostic.SA1304.severity = none # NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter +dotnet_diagnostic.SA1306.severity = none # FieldNamesMustBeginWithLowerCaseLetter +dotnet_diagnostic.SA1307.severity = none # AccessibleFieldsMustBeginWithUpperCaseLetter +dotnet_diagnostic.SA1311.severity = none # StaticReadonlyFieldsMustBeginWithUpperCaseLetter +dotnet_diagnostic.SA1312.severity = none # VariableNamesMustBeginWithLowerCaseLetter +dotnet_diagnostic.SA1313.severity = none # ParameterNamesMustBeginWithLowerCaseLetter + +# Rules that conflict with OpenRA project style conventions +dotnet_diagnostic.SA1101.severity = none # PrefixLocalCallsWithThis +dotnet_diagnostic.SA1107.severity = none # CodeMustNotContainMultipleStatementsOnOneLine +dotnet_diagnostic.SA1116.severity = none # SplitParametersMustStartOnLineAfterDeclaration +dotnet_diagnostic.SA1117.severity = none # ParametersMustBeOnSameLineOrSeparateLines +dotnet_diagnostic.SA1118.severity = none # ParameterMustNotSpanMultipleLines +dotnet_diagnostic.SA1122.severity = none # UseStringEmptyForEmptyStrings +dotnet_diagnostic.SA1124.severity = none # DoNotUseRegions +dotnet_diagnostic.SA1127.severity = none # GenericTypeConstraintsMustBeOnOwnLine +dotnet_diagnostic.SA1132.severity = none # DoNotCombineFields +dotnet_diagnostic.SA1135.severity = none # UsingDirectivesMustBeQualified +dotnet_diagnostic.SA1136.severity = none # EnumValuesShouldBeOnSeparateLines +dotnet_diagnostic.SA1200.severity = none # UsingDirectivesMustBePlacedCorrectly +dotnet_diagnostic.SA1201.severity = none # ElementsMustAppearInTheCorrectOrder +dotnet_diagnostic.SA1202.severity = none # ElementsMustBeOrderedByAccess +dotnet_diagnostic.SA1204.severity = none # StaticElementsMustAppearBeforeInstanceElements +dotnet_diagnostic.SA1214.severity = none # ReadonlyElementsMustAppearBeforeNonReadonlyElements +dotnet_diagnostic.SX1309.severity = none # FieldNamesMustBeginWithUnderscore +dotnet_diagnostic.SX1309S.severity = none # StaticFieldNamesMustBeginWithUnderscore +dotnet_diagnostic.SA1314.severity = none # TypeParameterNamesMustBeginWithT +dotnet_diagnostic.SA1400.severity = none # AccessModifierMustBeDeclared +dotnet_diagnostic.SA1401.severity = none # FieldsMustBePrivate +dotnet_diagnostic.SA1402.severity = none # FileMayOnlyContainASingleType +dotnet_diagnostic.SA1407.severity = none # ArithmeticExpressionsMustDeclarePrecedence +dotnet_diagnostic.SA1413.severity = none # UseTrailingCommasInMultiLineInitializers +dotnet_diagnostic.SA1501.severity = none # StatementMustNotBeOnSingleLine +dotnet_diagnostic.SA1502.severity = none # ElementMustNotBeOnSingleLine +dotnet_diagnostic.SA1503.severity = none # BracesMustNotBeOmitted +dotnet_diagnostic.SA1516.severity = none # ElementsMustBeSeparatedByBlankLine +dotnet_diagnostic.SA1519.severity = none # BracesMustNotBeOmittedFromMultiLineChildStatement +dotnet_diagnostic.SA1520.severity = none # UseBracesConsistently +dotnet_diagnostic.SA1600.severity = none # ElementsMustBeDocumented +dotnet_diagnostic.SA1601.severity = none # PartialElementsMustBeDocumented +dotnet_diagnostic.SA1602.severity = none # EnumerationItemsMustBeDocumented +dotnet_diagnostic.SA1611.severity = none # ElementParametersShouldBeDocumented +dotnet_diagnostic.SA1615.severity = none # ElementReturnValueShouldBeDocumented +dotnet_diagnostic.SA1618.severity = none # GenericTypeParametersShouldBeDocumented +dotnet_diagnostic.SA1623.severity = none # PropertySummaryDocumentationShouldMatchAccessors +dotnet_diagnostic.SA1633.severity = none # FileMustHaveHeader +dotnet_diagnostic.SA1642.severity = none # ConstructorSummaryDocumentationShouldBeginWithStandardText +dotnet_diagnostic.SA1649.severity = none # FileNameMustMatchTypeName + +#### Code Quality Rules +#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ + +# Below we enable specific rules by setting severity to warning. +# Rules are listed below with any options available. +# Options are commented out if they match the defaults. + +# Rule options that apply over multiple rules are set here. +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-quality-rule-options +dotnet_code_quality.api_surface = all + +### Design Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/design-warnings + +# Collections should implement generic interface. +#dotnet_code_quality.CA1010.additional_required_generic_interfaces = +dotnet_diagnostic.CA1010.severity = warning + +# Mark attributes with 'AttributeUsageAttribute'. +dotnet_diagnostic.CA1018.severity = warning + +# Override methods on comparable types. +dotnet_diagnostic.CA1036.severity = warning + +# Provide ObsoleteAttribute message. +dotnet_diagnostic.CA1041.severity = warning + +# Do not declare protected members in sealed types. +dotnet_diagnostic.CA1047.severity = warning + +# Declare types in namespaces. +dotnet_diagnostic.CA1050.severity = warning + +# Do not hide base class methods. +dotnet_diagnostic.CA1061.severity = warning + +# Exceptions should be public. +dotnet_diagnostic.CA1064.severity = warning + +# Implement 'IEquatable' when overriding 'Equals'. +dotnet_diagnostic.CA1066.severity = warning + +# Override 'Equals' when implementing 'IEquatable'. +dotnet_diagnostic.CA1067.severity = warning + +# 'CancellationToken' parameters must come last. +dotnet_diagnostic.CA1068.severity = warning + +# Do not declare event fields as virtual. +dotnet_diagnostic.CA1070.severity = warning + +### Documentation Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/documentation-warnings + +# Avoid using 'cref' tags with a prefix. +dotnet_diagnostic.CA1200.severity = warning + +### Globalization Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/globalization-warnings + +### Portability and Interoperability Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/interoperability-warnings + +# Do not use 'OutAttribute' on string parameters for P/Invokes. +dotnet_diagnostic.CA1417.severity = warning + +### Maintainability Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/maintainability-warnings -# Also handled by StyleCopAnalyzers - SA1024: ColonsMustBeSpacedCorrectly. -csharp_space_before_colon_in_inheritance_clause = true +# Use 'nameof' in place of string. +dotnet_diagnostic.CA1507.severity = warning -# Also handled by StyleCopAnalyzers - SA1000: KeywordsMustBeSpacedCorrectly. -csharp_space_after_keywords_in_control_flow_statements = true +### Naming Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/naming-warnings -# Leave code block on single line. -csharp_preserve_single_line_blocks = true +# Do not prefix enum values with type name. +dotnet_code_quality.CA1712.enum_values_prefix_trigger = AnyEnumValue +dotnet_diagnostic.CA1712.severity = warning -# Leave statements and member declarations on the same line. -csharp_preserve_single_line_statements = true +# Flags enums should have plural names. +dotnet_diagnostic.CA1714.severity = warning -# IDE0049, IDE-only counterpart of StyleCopAnalyzers - SA1121: UseBuiltInTypeAlias. -dotnet_style_predefined_type_for_member_access = true +# Only 'FlagsAttribute' enums should have plural names. +dotnet_diagnostic.CA1717.severity = warning -# IDE0049, IDE-only counterpart of StyleCopAnalyzers - SA1121: UseBuiltInTypeAlias. -dotnet_style_predefined_type_for_locals_parameters_members = true +### Performance Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings -## Others: +# Remove empty finalizers. +dotnet_diagnostic.CA1821.severity = warning -# Show an IDE warning when default access modifiers are explicitly specified. -dotnet_style_require_accessibility_modifiers = omit_if_default:warning +# Avoid unused private fields. +dotnet_diagnostic.CA1823.severity = warning -# Use 'var' instead of explicit type. -dotnet_diagnostic.IDE0007.severity = warning +# Avoid zero-length array allocations. +dotnet_diagnostic.CA1825.severity = warning -# Don't prefer braces (for one liners). -dotnet_diagnostic.IDE0011.severity = silent +# Use property instead of Linq Enumerable method. +#dotnet_code_quality.CA1826.exclude_ordefault_methods = false +dotnet_diagnostic.CA1826.severity = warning -# Object initialization can be simplified / Use object initializer. -dotnet_diagnostic.IDE0017.severity = warning +# Do not use Count/LongCount when Any can be used. +dotnet_diagnostic.CA1827.severity = warning -# Collection initialization can be simplified -dotnet_diagnostic.IDE0028.severity = warning +# Do not use CountAsync/LongCountAsync when AnyAsync can be used. +dotnet_diagnostic.CA1828.severity = warning -# Simplify 'default' expression -dotnet_diagnostic.IDE0034.severity = warning +# Use Length/Count property instead of Enumerable.Count method. +dotnet_diagnostic.CA1829.severity = warning -# Modifiers are not ordered. -dotnet_diagnostic.IDE0036.severity = warning +# Prefer strongly-typed Append and Insert method overloads on StringBuilder. +dotnet_diagnostic.CA1830.severity = warning -# Raise a warning on build when default access modifiers are explicitly specified. -dotnet_diagnostic.IDE0040.severity = warning +# Use AsSpan instead of Range-based indexers for string when appropriate. +dotnet_diagnostic.CA1831.severity = warning -# Make field readonly. -dotnet_diagnostic.IDE0044.severity = warning +# Use AsSpan or AsMemory instead of Range-based indexers for getting ReadOnlySpan or ReadOnlyMemory portion of an array. +dotnet_diagnostic.CA1832.severity = warning -# Unused private member. -dotnet_diagnostic.IDE0052.severity = warning +# Use AsSpan or AsMemory instead of Range-based indexers for getting Span or Memory portion of an array. +dotnet_diagnostic.CA1833.severity = warning -# Unnecessary value assignment. -dotnet_diagnostic.IDE0059.severity = warning +# Use StringBuilder.Append(char) for single character strings. +dotnet_diagnostic.CA1834.severity = warning -# Unused parameter. -dotnet_diagnostic.IDE0060.severity = warning +# Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes. +dotnet_diagnostic.CA1835.severity = warning -# Naming rule violation. -dotnet_diagnostic.IDE1006.severity = warning +# Prefer IsEmpty over Count when available. +dotnet_diagnostic.CA1836.severity = warning -# Avoid unnecessary zero-length array allocations. -dotnet_diagnostic.CA1825.severity = warning +# Use Environment.ProcessId instead of Process.GetCurrentProcess().Id. +dotnet_diagnostic.CA1837.severity = warning -# Do not use Enumerable methods on indexable collections. Instead use the collection directly. -dotnet_diagnostic.CA1826.severity = warning +# Avoid StringBuilder parameters for P/Invokes. +dotnet_diagnostic.CA1838.severity = warning -# Count() is used where Any() could be used instead to improve performance. -dotnet_diagnostic.CA1827.severity = warning +# Use Environment.ProcessPath instead of Process.GetCurrentProcess().MainModule.FileName. +dotnet_diagnostic.CA1839.severity = warning -# Use Length/Count property instead of Enumerable.Count method. -dotnet_diagnostic.CA1829.severity = warning +# Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId. +dotnet_diagnostic.CA1840.severity = warning + +# Prefer Dictionary Contains methods. +dotnet_diagnostic.CA1841.severity = warning + +# Do not use 'WhenAll' with a single task. +dotnet_diagnostic.CA1842.severity = warning + +# Do not use 'WaitAll' with a single task. +dotnet_diagnostic.CA1843.severity = warning + +# Provide memory-based overrides of async methods when subclassing 'Stream'. +dotnet_diagnostic.CA1844.severity = warning + +# Use span-based 'string.Concat'. +dotnet_diagnostic.CA1845.severity = warning + +# Prefer AsSpan over Substring. +dotnet_diagnostic.CA1846.severity = warning # Use string.Contains(char) instead of string.Contains(string) with single characters. dotnet_diagnostic.CA1847.severity = warning -; 4-column tab indentation -[*.yaml] -indent_style = tab -indent_size = 4 +# Call async methods when in an async method. +dotnet_diagnostic.CA1849.severity = warning + +# Prefer static HashData method over ComputeHash. (Not available on mono) +dotnet_diagnostic.CA1850.severity = none + +# Unnecessary call to 'Dictionary.ContainsKey(key)'. +dotnet_diagnostic.CA1853.severity = warning + +# Use Span.Clear() instead of Span.Fill(). +dotnet_diagnostic.CA1855.severity = warning + +# Use StartsWith instead of IndexOf. +dotnet_diagnostic.CA1858.severity = warning + +# Avoid using 'Enumerable.Any()' extension method. +dotnet_diagnostic.CA1860.severity = warning + +### Reliability Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/reliability-warnings + +# Do not assign property within its setter. +dotnet_diagnostic.CA2011.severity = warning + +# Use ValueTasks correctly. +dotnet_diagnostic.CA2012.severity = warning + +# Do not use ReferenceEquals with value types. +dotnet_diagnostic.CA2013.severity = warning + +# Do not use stackalloc in loops. +dotnet_diagnostic.CA2014.severity = warning + +# Forward the CancellationToken parameter to methods that take one. +dotnet_diagnostic.CA2016.severity = warning + +# The 'count' argument to Buffer.BlockCopy should specify the number of bytes to copy. +dotnet_diagnostic.CA2018.severity = warning + +# ThreadStatic fields should not use inline initialization. +dotnet_diagnostic.CA2019.severity = warning + +### Security Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/security-warnings + +# Do Not Use Broken Cryptographic Algorithms. +dotnet_diagnostic.CA5351.severity = warning + +### Usage Rules +### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/usage-warnings + +# Rethrow to preserve stack details. +dotnet_diagnostic.CA2200.severity = warning + +# Initialize value type static fields inline. +dotnet_diagnostic.CA2207.severity = warning + +# Instantiate argument exceptions correctly. +dotnet_diagnostic.CA2208.severity = warning + +# Dispose methods should call base class dispose. +dotnet_diagnostic.CA2215.severity = warning + +# Disposable types should declare finalizer. +dotnet_diagnostic.CA2216.severity = warning + +# Override GetHashCode on overriding Equals. +dotnet_diagnostic.CA2218.severity = warning + +# Overload operator equals on overriding ValueType.Equals. +dotnet_diagnostic.CA2231.severity = warning + +# Provide correct arguments to formatting methods. +#dotnet_code_quality.CA2241.additional_string_formatting_methods = +dotnet_code_quality.CA2241.try_determine_additional_string_formatting_methods_automatically = true +dotnet_diagnostic.CA2241.severity = warning + +# Test for NaN correctly. +dotnet_diagnostic.CA2242.severity = warning + +# Attribute string literals should parse correctly. +dotnet_diagnostic.CA2243.severity = warning + +# Do not duplicate indexed element initializations. +dotnet_diagnostic.CA2244.severity = warning + +# Do not assign a property to itself. +dotnet_diagnostic.CA2245.severity = warning + +# Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum. +dotnet_diagnostic.CA2247.severity = warning + +# Provide correct enum argument to Enum.HasFlag. +dotnet_diagnostic.CA2248.severity = warning + +# Use ThrowIfCancellationRequested. +dotnet_diagnostic.CA2250.severity = warning + +# Ensure ThreadStatic is only used with static fields. +dotnet_diagnostic.CA2259.severity = warning + +### Roslynator +### https://github.com/JosefPihrt/Roslynator/tree/main/docs/analyzers + +# Below we enable specific rules by setting severity to warning. # Use 'Count' property instead of 'Any' method. dotnet_diagnostic.RCS1080.severity = warning diff --git a/OpenRA.Mods.Mobius/RemasterPackages.cs b/OpenRA.Mods.Mobius/RemasterPackages.cs index f129f00..765593c 100644 --- a/OpenRA.Mods.Mobius/RemasterPackages.cs +++ b/OpenRA.Mods.Mobius/RemasterPackages.cs @@ -24,7 +24,7 @@ public sealed class RemasterModContent : IGlobalModData public readonly Dictionary Packages; [FieldLoader.Ignore] - readonly Dictionary sources = new Dictionary(); + readonly Dictionary sources = new(); public RemasterModContent(MiniYaml yaml) { diff --git a/OpenRA.Mods.Mobius/RemasterSpriteSequence.cs b/OpenRA.Mods.Mobius/RemasterSpriteSequence.cs index 10b541b..8be7f86 100644 --- a/OpenRA.Mods.Mobius/RemasterSpriteSequence.cs +++ b/OpenRA.Mods.Mobius/RemasterSpriteSequence.cs @@ -19,24 +19,10 @@ namespace OpenRA.Mods.Cnc.Graphics { public class RemasterSpriteSequenceLoader : ClassicTilesetSpecificSpriteSequenceLoader { - public readonly string DefaultSpriteExtension = ".shp"; - public readonly Dictionary TilesetExtensions = new Dictionary(); - public readonly Dictionary TilesetCodes = new Dictionary(); public readonly float ClassicUpscaleFactor = 5.333333f; public RemasterSpriteSequenceLoader(ModData modData) - : base(modData) - { - var metadata = modData.Manifest.Get().Metadata; - if (metadata.TryGetValue("DefaultSpriteExtension", out var yaml)) - DefaultSpriteExtension = yaml.Value; - - if (metadata.TryGetValue("TilesetExtensions", out yaml)) - TilesetExtensions = yaml.ToDictionary(kv => kv.Value); - - if (metadata.TryGetValue("TilesetCodes", out yaml)) - TilesetCodes = yaml.ToDictionary(kv => kv.Value); - } + : base(modData) { } public override ISpriteSequence CreateSequence(ModData modData, string tileset, SpriteCache cache, string image, string sequence, MiniYaml data, MiniYaml defaults) { @@ -48,102 +34,142 @@ public override ISpriteSequence CreateSequence(ModData modData, string tileset, "that come with first-generation Westwood titles.")] public class RemasterSpriteSequence : ClassicTilesetSpecificSpriteSequence { - [Desc("Dictionary of with tileset name to override -> tileset name to use instead.")] - static readonly SpriteSequenceField> TilesetOverrides = new SpriteSequenceField>(nameof(TilesetOverrides), null); - - [Desc("Use `TilesetCodes` as defined in `mod.yaml` to add a letter as a second character " + - "into the sprite filename like the Westwood 2.5D titles did for tileset-specific variants.")] - static readonly SpriteSequenceField UseTilesetCode = new SpriteSequenceField(nameof(UseTilesetCode), false); - - [Desc("Append a tileset-specific extension to the file name " + - "- either as defined in `mod.yaml`'s `TilesetExtensions` (if `UseTilesetExtension` is used) " + - "or the default hardcoded one for this sequence type (.shp).")] - static readonly SpriteSequenceField AddExtension = new SpriteSequenceField(nameof(AddExtension), true); - - [Desc("Whether `mod.yaml`'s `TilesetExtensions` should be used with the sequence's file name.")] - static readonly SpriteSequenceField UseTilesetExtension = new SpriteSequenceField(nameof(UseTilesetExtension), false); - [Desc("File name of the remastered sprite to use for this sequence.")] - static readonly SpriteSequenceField RemasteredFilename = new SpriteSequenceField(nameof(RemasteredFilename), null); + static readonly SpriteSequenceField RemasteredFilename = new(nameof(RemasteredFilename), null); [Desc("Dictionary of : filename to override the RemasteredFilename key.")] - static readonly SpriteSequenceField> RemasteredTilesetFilenames = new SpriteSequenceField>(nameof(RemasteredTilesetFilenames), null); + static readonly SpriteSequenceField> RemasteredTilesetFilenames = new(nameof(RemasteredTilesetFilenames), null); [Desc("File name pattern to build the remastered sprite to use for this sequence.")] - static readonly SpriteSequenceField RemasteredFilenamePattern = new SpriteSequenceField(nameof(RemasteredFilenamePattern), null); + static readonly SpriteSequenceField RemasteredFilenamePattern = new(nameof(RemasteredFilenamePattern), null); [Desc("Dictionary of : to override the RemasteredFilenamePattern key.")] - static readonly SpriteSequenceField> RemasteredTilesetFilenamesPattern = new SpriteSequenceField>(nameof(RemasteredTilesetFilenamesPattern), null); + static readonly SpriteSequenceField> RemasteredTilesetFilenamesPattern = new(nameof(RemasteredTilesetFilenamesPattern), null); [Desc("File name pattern of the sprite to mask the remastered sprite.")] - static readonly SpriteSequenceField RemasteredMaskFilename = new SpriteSequenceField(nameof(RemasteredMaskFilename), null); + static readonly SpriteSequenceField RemasteredMaskFilename = new(nameof(RemasteredMaskFilename), null); [Desc("Change the position in-game on X, Y, Z.")] - protected static readonly SpriteSequenceField RemasteredOffset = new SpriteSequenceField(nameof(RemasteredOffset), float3.Zero); + protected static readonly SpriteSequenceField RemasteredOffset = new(nameof(RemasteredOffset), float3.Zero); [Desc("Frame index to start from.")] - protected static readonly SpriteSequenceField RemasteredStart = new SpriteSequenceField(nameof(RemasteredStart), null); + protected static readonly SpriteSequenceField RemasteredStart = new(nameof(RemasteredStart), null); [Desc("Number of frames to use. Does not have to be the total amount the sprite sheet has.")] - protected static readonly SpriteSequenceField RemasteredLength = new SpriteSequenceField(nameof(RemasteredLength), null); + protected static readonly SpriteSequenceField RemasteredLength = new(nameof(RemasteredLength), null); [Desc("Time (in milliseconds at default game speed) to wait until playing the next frame in the animation.")] - protected static readonly SpriteSequenceField RemasteredTick = new SpriteSequenceField(nameof(RemasteredTick), null); + protected static readonly SpriteSequenceField RemasteredTick = new(nameof(RemasteredTick), null); [Desc("Adjusts the rendered size of the sprite")] - protected static readonly SpriteSequenceField RemasteredScale = new SpriteSequenceField(nameof(RemasteredScale), null); + protected static readonly SpriteSequenceField RemasteredScale = new(nameof(RemasteredScale), null); static readonly int[] FirstFrame = { 0 }; bool hasRemasteredSprite = true; - public RemasterSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) - : base(cache, loader, image, sequence, data, defaults) + IEnumerable ParseRemasterFilenames(ModData modData, string tileset, int[] frames, MiniYaml data, MiniYaml defaults) { - start = LoadField(RemasteredStart, data, defaults) ?? start; - tick = LoadField(RemasteredTick, data, defaults) ?? tick; - scale = LoadField(RemasteredScale, data, defaults) ?? scale; + string filename = null; + MiniYamlNode.SourceLocation location = default; + var remasteredTilesetFilenamesPatternNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenamesPattern.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenamesPattern.Key); + if (remasteredTilesetFilenamesPatternNode != null) + { + var tilesetNode = remasteredTilesetFilenamesPatternNode.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + if (tilesetNode != null) + { + var patternStart = LoadField("Start", 0, tilesetNode.Value); + var patternCount = LoadField("Count", 1, tilesetNode.Value); - if (LoadField(RemasteredLength.Key, null, data, defaults) != "*") - length = LoadField(RemasteredLength, data, defaults) ?? length; - else - length = null; - } + return Enumerable.Range(patternStart, patternCount).Select(i => + new ReservationInfo(string.Format(tilesetNode.Value.Value, i), FirstFrame, FirstFrame, tilesetNode.Location)); + } + } - string ResolveTilesetId(string tileset, MiniYaml data, MiniYaml defaults) - { - var yaml = data.Nodes.FirstOrDefault(n => n.Key == TilesetOverrides.Key) ?? defaults?.Nodes.FirstOrDefault(n => n.Key == TilesetOverrides.Key); - if (yaml != null) + var remasteredTilesetFilenamesNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key); + if (!string.IsNullOrEmpty(remasteredTilesetFilenamesNode?.Value.Value)) { - var tsNode = yaml.Value.Nodes.FirstOrDefault(n => n.Key == tileset); - if (tsNode != null) - return tsNode.Value.Value; + var tilesetNode = remasteredTilesetFilenamesNode.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + if (tilesetNode != null) + { + filename = tilesetNode.Value.Value; + location = tilesetNode.Location; + } + } + else + { + var remasteredFilenamePatternNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredFilenamePattern.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredFilenamePattern.Key); + if (!string.IsNullOrEmpty(remasteredFilenamePatternNode?.Value.Value)) + { + var patternStart = LoadField("Start", 0, remasteredFilenamePatternNode.Value); + var patternCount = LoadField("Count", 1, remasteredFilenamePatternNode.Value); + + return Enumerable.Range(patternStart, patternCount).Select(i => + new ReservationInfo(string.Format(remasteredFilenamePatternNode.Value.Value, i), + FirstFrame, FirstFrame, remasteredFilenamePatternNode.Location)); + } } - return tileset; + filename ??= LoadField(RemasteredFilename, data, defaults, out location); + if (filename != null) + { + // Only request the subset of frames that we actually need + var loadFrames = CalculateFrameIndices(start, length, stride ?? length ?? 0, facings, frames, transpose, reverseFacings, shadowStart); + return new[] { new ReservationInfo(filename, loadFrames, frames, location) }; + } + else + { + hasRemasteredSprite = false; + return ParseFilenames(modData, tileset, frames, data, defaults); + } } - string GetSpriteSrc(string tileset, string spriteName, MiniYaml data, MiniYaml defaults) + IEnumerable ParseRemasterCombineFilenames(ModData modData, string tileset, int[] frames, MiniYaml data) { - var loader = (RemasterSpriteSequenceLoader)Loader; + string filename = null; + MiniYamlNode.SourceLocation location = default; - if (LoadField(UseTilesetCode, data, defaults)) + var node = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key); + if (node != null) { - if (loader.TilesetCodes.TryGetValue(ResolveTilesetId(tileset, data, defaults), out var code)) - spriteName = spriteName.Substring(0, 1) + code + spriteName.Substring(2, spriteName.Length - 2); + var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); + if (tilesetNode != null) + { + filename = tilesetNode.Value.Value; + location = tilesetNode.Location; + } } - if (LoadField(AddExtension, data, defaults)) + filename ??= LoadField(RemasteredFilename, data, null, out location); + if (frames == null && LoadField("Length", null, data) != "*") { - var useTilesetExtension = LoadField(UseTilesetExtension, data, defaults); - - if (useTilesetExtension && loader.TilesetExtensions.TryGetValue(ResolveTilesetId(tileset, data, defaults), out var tilesetExtension)) - return spriteName + tilesetExtension; + var subStart = LoadField("Start", 0, data); + var subLength = LoadField("Length", 1, data); + frames = Exts.MakeArray(subLength, i => subStart + i); + } - return spriteName + loader.DefaultSpriteExtension; + if (filename != null) + { + return new[] { new ReservationInfo(filename, frames, frames, location) }; } + else + { + hasRemasteredSprite = false; + return ParseCombineFilenames(modData, tileset, frames, data); + } + } - return spriteName; + public RemasterSpriteSequence(SpriteCache cache, ISpriteSequenceLoader loader, string image, string sequence, MiniYaml data, MiniYaml defaults) + : base(cache, loader, image, sequence, data, defaults) + { + start = LoadField(RemasteredStart, data, defaults) ?? start; + tick = LoadField(RemasteredTick, data, defaults) ?? tick; + scale = LoadField(RemasteredScale, data, defaults) ?? scale; + + if (LoadField(RemasteredLength.Key, null, data, defaults) != "*") + length = LoadField(RemasteredLength, data, defaults) ?? length; + else + length = null; } int? remasteredMaskToken; @@ -174,13 +200,13 @@ public override void ReserveSprites(ModData modData, string tileset, SpriteCache var subFlipY = LoadField(FlipY, subData, NoData); var subFrames = LoadField(Frames, data); - foreach ((var subFilename, var subLoadFrames, var subUseFrames, var subLocation) in ParseRemasterCombineFilenames(tileset, subFrames, combineNode.Value.Nodes[i].Key, subData)) + foreach (var f in ParseRemasterCombineFilenames(modData, tileset, subFrames, subData)) { int token; if (remasteredMaskToken != null) - token = cache.ReserveFrames(subFilename, subLoadFrames, subLocation); + token = cache.ReserveFrames(f.Filename, f.LoadFrames, f.Location); else - token = cache.ReserveSprites(subFilename, subLoadFrames, subLocation); + token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location); spritesToLoad.Add(new SpriteReservation { @@ -190,20 +216,20 @@ public override void ReserveSprites(ModData modData, string tileset, SpriteCache FlipY = subFlipY ^ flipY, BlendMode = blendMode, ZRamp = zRamp, - Frames = subUseFrames + Frames = f.Frames }); } } } else { - foreach ((var filename, var loadFrames, var useFrames, var location) in ParseRemasterFilenames(tileset, frames, data, defaults)) + foreach (var f in ParseRemasterFilenames(modData, tileset, frames, data, defaults)) { int token; if (remasteredMaskToken != null) - token = cache.ReserveFrames(filename, loadFrames, location); + token = cache.ReserveFrames(f.Filename, f.LoadFrames, f.Location); else - token = cache.ReserveSprites(filename, loadFrames, location); + token = cache.ReserveSprites(f.Filename, f.LoadFrames, f.Location); spritesToLoad.Add(new SpriteReservation { @@ -213,7 +239,7 @@ public override void ReserveSprites(ModData modData, string tileset, SpriteCache FlipY = flipY, BlendMode = blendMode, ZRamp = zRamp, - Frames = useFrames, + Frames = f.Frames, }); } } @@ -314,7 +340,7 @@ public override void ResolveSprites(SpriteCache cache) }).ToArray(); } - length = length ?? allSprites.Length - start; + length ??= allSprites.Length - start; if (alpha != null) { @@ -327,14 +353,14 @@ public override void ResolveSprites(SpriteCache cache) alpha = Exts.MakeArray(length.Value, i => float2.Lerp(1f, 0f, i / (length.Value - 1f))); // Reindex sprites to order facings anti-clockwise and remove unused frames - var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, null, transpose, reverseFacings).ToList(); + var index = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, null, transpose, reverseFacings, -1); if (reverses) { index.AddRange(index.Skip(1).Take(length.Value - 2).Reverse()); length = 2 * length - 2; } - if (!index.Any()) + if (index.Count == 0) throw new YamlException($"Sequence {image}.{Name} does not define any frames."); var minIndex = index.Min(); @@ -349,130 +375,6 @@ public override void ResolveSprites(SpriteCache cache) bounds = sprites.Concat(shadowSprites ?? Enumerable.Empty()).Select(OffsetSpriteBounds).Union(); } - IEnumerable<(string Filename, int[] loadFrames, int[] useFrames, MiniYamlNode.SourceLocation location)> ParseRemasterFilenames(string tileset, int[] frames, MiniYaml data, MiniYaml defaults) - { - string filename = null; - MiniYamlNode.SourceLocation location = default; - var remasteredTilesetFilenamesPatternNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenamesPattern.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenamesPattern.Key); - if (remasteredTilesetFilenamesPatternNode != null) - { - var tilesetNode = remasteredTilesetFilenamesPatternNode.Value.Nodes.FirstOrDefault(n => n.Key == tileset); - if (tilesetNode != null) - { - var patternStart = LoadField("Start", 0, tilesetNode.Value); - var patternCount = LoadField("Count", 1, tilesetNode.Value); - - return Enumerable.Range(patternStart, patternCount) - .Select(i => (string.Format(tilesetNode.Value.Value, i), FirstFrame, FirstFrame, tilesetNode.Location)); - } - } - - var remasteredTilesetFilenamesNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key); - if (!string.IsNullOrEmpty(remasteredTilesetFilenamesNode?.Value.Value)) - { - var tilesetNode = remasteredTilesetFilenamesNode.Value.Nodes.FirstOrDefault(n => n.Key == tileset); - if (tilesetNode != null) - { - filename = tilesetNode.Value.Value; - location = tilesetNode.Location; - } - } - else - { - var remasteredFilenamePatternNode = data.Nodes.FirstOrDefault(n => n.Key == RemasteredFilenamePattern.Key) ?? defaults.Nodes.FirstOrDefault(n => n.Key == RemasteredFilenamePattern.Key); - if (!string.IsNullOrEmpty(remasteredFilenamePatternNode?.Value.Value)) - { - var patternStart = LoadField("Start", 0, remasteredFilenamePatternNode.Value); - var patternCount = LoadField("Count", 1, remasteredFilenamePatternNode.Value); - - return Enumerable.Range(patternStart, patternCount) - .Select(i => (string.Format(remasteredFilenamePatternNode.Value.Value, i), FirstFrame, FirstFrame, remasteredFilenamePatternNode.Location)); - } - } - - filename = filename ?? LoadField(RemasteredFilename, data, defaults, out location); - if (filename != null) - { - // Only request the subset of frames that we actually need - int[] loadFrames = null; - if (length != null) - { - loadFrames = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, frames, transpose, reverseFacings); - if (shadowStart >= 0) - loadFrames = loadFrames.Concat(loadFrames.Select(i => i + shadowStart - start)).ToArray(); - } - - return new[] { (filename, loadFrames, frames, location) }; - } - else - { - hasRemasteredSprite = false; - filename = GetSpriteSrc(tileset, data?.Value ?? defaults?.Value ?? image, data, defaults); - location = (data.Nodes.FirstOrDefault() ?? defaults.Nodes.FirstOrDefault())?.Location ?? default; - int[] loadFrames = null; - - // Only request the subset of frames that we actually need - if (length != null) - { - loadFrames = CalculateFrameIndices(start, length.Value, stride ?? length.Value, facings, frames, transpose, reverseFacings); - if (shadowStart >= 0) - loadFrames = loadFrames.Concat(loadFrames.Select(i => i + shadowStart - start)).ToArray(); - } - - return new[] { (filename, loadFrames, frames, location) }; - } - } - - IEnumerable<(string Filename, int[] loadFrames, int[] useFrames, MiniYamlNode.SourceLocation location)> ParseRemasterCombineFilenames(string tileset, int[] frames, string key, MiniYaml data) - { - string filename = null; - MiniYamlNode.SourceLocation location = default; - - var node = data.Nodes.FirstOrDefault(n => n.Key == RemasteredTilesetFilenames.Key); - if (node != null) - { - var tilesetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == tileset); - if (tilesetNode != null) - { - filename = tilesetNode.Value.Value; - location = tilesetNode.Location; - } - } - - filename = filename ?? LoadField(RemasteredFilename, data, null, out location); - if (frames == null) - { - if (LoadField("Length", null, data) != "*") - { - var subStart = LoadField("Start", 0, data); - var subLength = LoadField("Length", 1, data); - frames = Exts.MakeArray(subLength, i => subStart + i); - } - } - - if (filename != null) - { - yield return (filename, frames, frames, location); - yield break; - } - - hasRemasteredSprite = false; - filename = GetSpriteSrc(tileset, key, data, data); - location = data.Nodes.FirstOrDefault()?.Location ?? default; - - if (frames == null) - { - if (LoadField("Length", null, data) != "*") - { - var subStart = LoadField("Start", 0, data); - var subLength = LoadField("Length", 1, data); - frames = Exts.MakeArray(subLength, i => subStart + i); - } - } - - yield return (filename, frames, frames, location); - } - protected override float GetScale() { if (!hasRemasteredSprite) diff --git a/OpenRA.Mods.Mobius/Terrain/RemasterTerrain.cs b/OpenRA.Mods.Mobius/Terrain/RemasterTerrain.cs index 006a257..1d95fa6 100644 --- a/OpenRA.Mods.Mobius/Terrain/RemasterTerrain.cs +++ b/OpenRA.Mods.Mobius/Terrain/RemasterTerrain.cs @@ -57,7 +57,7 @@ public class RemasterTerrain : ITemplatedTerrainInfo, ITerrainInfoNotifyMapCreat [FieldLoader.Ignore] public readonly TerrainTypeInfo[] TerrainInfo; - readonly Dictionary terrainIndexByType = new Dictionary(); + readonly Dictionary terrainIndexByType = new(); readonly byte defaultWalkableTerrainIndex; public RemasterTerrain(IReadOnlyFileSystem fileSystem, string filepath) @@ -82,7 +82,7 @@ public RemasterTerrain(IReadOnlyFileSystem fileSystem, string filepath) var tt = TerrainInfo[i].Type; if (terrainIndexByType.ContainsKey(tt)) - throw new YamlException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath)); + throw new YamlException($"Duplicate terrain type '{tt}' in '{filepath}'."); terrainIndexByType.Add(tt, i); } @@ -104,7 +104,7 @@ public byte GetTerrainIndex(string type) if (terrainIndexByType.TryGetValue(type, out var index)) return index; - throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type)); + throw new InvalidDataException($"Tileset '{Id}' lacks terrain type '{type}'"); } public byte GetTerrainIndex(TerrainTile r) @@ -147,7 +147,7 @@ public bool TryGetTileInfo(TerrainTile r, out TerrainTileInfo info) IEnumerable ITerrainInfo.RestrictedPlayerColors { get { return TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); } } float ITerrainInfo.MinHeightColorBrightness => 1.0f; float ITerrainInfo.MaxHeightColorBrightness => 1.0f; - TerrainTile ITerrainInfo.DefaultTerrainTile => new TerrainTile(Templates.First().Key, 0); + TerrainTile ITerrainInfo.DefaultTerrainTile => new(Templates.First().Key, 0); string[] ITemplatedTerrainInfo.EditorTemplateOrder => EditorTemplateOrder; IReadOnlyDictionary ITemplatedTerrainInfo.Templates => Templates; diff --git a/OpenRA.Mods.Mobius/Terrain/RemasterTileCache.cs b/OpenRA.Mods.Mobius/Terrain/RemasterTileCache.cs index 8ceb06f..eca44ac 100644 --- a/OpenRA.Mods.Mobius/Terrain/RemasterTileCache.cs +++ b/OpenRA.Mods.Mobius/Terrain/RemasterTileCache.cs @@ -20,18 +20,16 @@ public sealed class RemasterTileCache : IDisposable { static readonly int[] FirstFrame = { 0 }; - readonly Dictionary> sprites = new Dictionary>(); - readonly Dictionary scale = new Dictionary(); - readonly SpriteCache spriteCache; - readonly Sprite blankSprite; - - public SpriteCache SpriteCache => spriteCache; + readonly Dictionary> sprites = new(); + readonly Dictionary scale = new(); + public SpriteCache SpriteCache { get; } + Sprite BlankSprite { get; } public RemasterTileCache(RemasterTerrain terrainInfo) { - spriteCache = new SpriteCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders, terrainInfo.BgraSheetSize, terrainInfo.IndexedSheetSize, 0); + SpriteCache = new SpriteCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders, terrainInfo.BgraSheetSize, terrainInfo.IndexedSheetSize, 0); - var blankToken = spriteCache.ReserveSprites(terrainInfo.BlankTile, FirstFrame, default); + var blankToken = SpriteCache.ReserveSprites(terrainInfo.BlankTile, FirstFrame, default); var remasteredSpriteReservations = new Dictionary>(); foreach (var t in terrainInfo.Templates) @@ -43,7 +41,7 @@ public RemasterTileCache(RemasterTerrain terrainInfo) { foreach (var kv in templateInfo.RemasteredFilenames) templateTokens[kv.Key] = kv.Value - .Select(f => spriteCache.ReserveSprites(f, FirstFrame, default)) + .Select(f => SpriteCache.ReserveSprites(f, FirstFrame, default)) .ToArray(); scale[t.Key] = 1f; } @@ -54,7 +52,7 @@ public RemasterTileCache(RemasterTerrain terrainInfo) if (t.Value[i] == null) continue; - templateTokens[i] = new[] { spriteCache.ReserveSprites(templateInfo.Filename, new[] { i }, default) }; + templateTokens[i] = new[] { SpriteCache.ReserveSprites(templateInfo.Filename, new[] { i }, default) }; } scale[t.Key] = terrainInfo.ClassicUpscaleFactor; @@ -63,31 +61,31 @@ public RemasterTileCache(RemasterTerrain terrainInfo) remasteredSpriteReservations[t.Key] = templateTokens; } - spriteCache.LoadReservations(Game.ModData); + SpriteCache.LoadReservations(Game.ModData); - blankSprite = spriteCache.ResolveSprites(blankToken).First(s => s != null); + BlankSprite = SpriteCache.ResolveSprites(blankToken).First(s => s != null); foreach (var kv in remasteredSpriteReservations) { sprites[kv.Key] = new Dictionary(); foreach (var tokens in kv.Value) sprites[kv.Key][tokens.Key] = tokens.Value - .Select(t => spriteCache.ResolveSprites(t).FirstOrDefault(s => s != null)) + .Select(t => SpriteCache.ResolveSprites(t).FirstOrDefault(s => s != null)) .ToArray(); } } public bool HasTileSprite(TerrainTile r, int frame) { - return TileSprite(r, frame) != blankSprite; + return TileSprite(r, frame) != BlankSprite; } public Sprite TileSprite(TerrainTile r, int frame) { if (!sprites.TryGetValue(r.Type, out var templateSprites)) - return blankSprite; + return BlankSprite; if (!templateSprites.TryGetValue(r.Index, out var tileSprites)) - return blankSprite; + return BlankSprite; return tileSprites[frame % tileSprites.Length]; } @@ -100,11 +98,11 @@ public float TileScale(TerrainTile r) return templateScale; } - public Sprite MissingTile => blankSprite; + public Sprite MissingTile => BlankSprite; public void Dispose() { - spriteCache.Dispose(); + SpriteCache.Dispose(); } } } diff --git a/OpenRA.Mods.Mobius/Traits/Palettes/ColorPickerColorShift.cs b/OpenRA.Mods.Mobius/Traits/Palettes/ColorPickerColorShift.cs index 1bd267c..519fa02 100644 --- a/OpenRA.Mods.Mobius/Traits/Palettes/ColorPickerColorShift.cs +++ b/OpenRA.Mods.Mobius/Traits/Palettes/ColorPickerColorShift.cs @@ -37,37 +37,49 @@ class ColorPickerColorShiftInfo : TraitInfo [Desc("Saturation reference for the color shift.")] public readonly float ReferenceSaturation = 0.925f; + [Desc("Value reference for the color shift.")] + public readonly float ReferenceValue = 0.95f; + public override object Create(ActorInitializer init) { return new ColorPickerColorShift(this); } } class ColorPickerColorShift : ILoadsPalettes, ITickRender { readonly ColorPickerColorShiftInfo info; - readonly ColorPickerManagerInfo colorManager; Color color; + Color preferredColor; public ColorPickerColorShift(ColorPickerColorShiftInfo info) { - // All users need to use the same TraitInfo instance, chosen as the default mod rules - colorManager = Game.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo(); this.info = info; + + // All users need to use the same TraitInfo instance, chosen as the default mod rules + var colorManager = Game.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo(); + colorManager.OnColorPickerColorUpdate += c => preferredColor = c; + preferredColor = Game.Settings.Player.Color; } void ILoadsPalettes.LoadPalettes(WorldRenderer wr) { - color = colorManager.Color; - var (_, h, s, _) = color.ToAhsv(); - wr.SetPaletteColorShift(info.BasePalette, h - info.ReferenceHue, s - info.ReferenceSaturation, info.MinHue, info.MaxHue); + color = preferredColor; + var (r, g, b) = color.ToLinear(); + var (h, s, v) = Color.RgbToHsv(r, g, b); + wr.SetPaletteColorShift(info.BasePalette, + h - info.ReferenceHue, s - info.ReferenceSaturation, v / info.ReferenceValue, + info.MinHue, info.MaxHue); } void ITickRender.TickRender(WorldRenderer wr, Actor self) { - if (color == colorManager.Color) + if (color == preferredColor) return; - color = colorManager.Color; - var (_, h, s, _) = color.ToAhsv(); - wr.SetPaletteColorShift(info.BasePalette, h - info.ReferenceHue, s - info.ReferenceSaturation, info.MinHue, info.MaxHue); + color = preferredColor; + var (r, g, b) = color.ToLinear(); + var (h, s, v) = Color.RgbToHsv(r, g, b); + wr.SetPaletteColorShift(info.BasePalette, + h - info.ReferenceHue, s - info.ReferenceSaturation, v / info.ReferenceValue, + info.MinHue, info.MaxHue); } } } diff --git a/OpenRA.Mods.Mobius/Traits/Palettes/FixedColorShift.cs b/OpenRA.Mods.Mobius/Traits/Palettes/FixedColorShift.cs index 99e6571..2b35668 100644 --- a/OpenRA.Mods.Mobius/Traits/Palettes/FixedColorShift.cs +++ b/OpenRA.Mods.Mobius/Traits/Palettes/FixedColorShift.cs @@ -39,6 +39,9 @@ public class FixedColorShiftInfo : TraitInfo [Desc("Saturation reference for the color shift.")] public readonly float ReferenceSaturation = 0.925f; + [Desc("Value reference for the color shift.")] + public readonly float ReferenceValue = 0.95f; + public override object Create(ActorInitializer init) { return new FixedColorShift(this); } } @@ -53,8 +56,11 @@ public FixedColorShift(FixedColorShiftInfo info) public void LoadPalettes(WorldRenderer wr) { - var (_, h, s, _) = info.Color.ToAhsv(); - wr.SetPaletteColorShift(info.BasePalette, h - info.ReferenceHue, s - info.ReferenceSaturation, info.MinHue, info.MaxHue); + var (r, g, b) = info.Color.ToLinear(); + var (h, s, v) = Color.RgbToHsv(r, g, b); + wr.SetPaletteColorShift(info.BasePalette, + h - info.ReferenceHue, s - info.ReferenceSaturation, v / info.ReferenceValue, + info.MinHue, info.MaxHue); } } } diff --git a/OpenRA.Mods.Mobius/Traits/Palettes/FixedPlayerColorShift.cs b/OpenRA.Mods.Mobius/Traits/Palettes/FixedPlayerColorShift.cs index e428b80..c375075 100644 --- a/OpenRA.Mods.Mobius/Traits/Palettes/FixedPlayerColorShift.cs +++ b/OpenRA.Mods.Mobius/Traits/Palettes/FixedPlayerColorShift.cs @@ -42,7 +42,7 @@ public FixedPlayerColorShift(FixedPlayerColorShiftInfo info) public void LoadPlayerPalettes(WorldRenderer wr, string playerName, Color color, bool replaceExisting) { if (info.PlayerIndex.TryGetValue(playerName, out var shift)) - wr.SetPaletteColorShift(info.BasePalette + playerName, shift[0], shift[1], shift[2], shift[3]); + wr.SetPaletteColorShift(info.BasePalette + playerName, shift[0], shift[1], shift[2], shift[3], shift[4]); } } } diff --git a/OpenRA.Mods.Mobius/Traits/Palettes/PlayerColorShift.cs b/OpenRA.Mods.Mobius/Traits/Palettes/PlayerColorShift.cs index 77b1cdd..d73411b 100644 --- a/OpenRA.Mods.Mobius/Traits/Palettes/PlayerColorShift.cs +++ b/OpenRA.Mods.Mobius/Traits/Palettes/PlayerColorShift.cs @@ -36,6 +36,9 @@ public class PlayerColorShiftInfo : TraitInfo [Desc("Saturation reference for the color shift.")] public readonly float ReferenceSaturation = 0.925f; + [Desc("Value reference for the color shift.")] + public readonly float ReferenceValue = 0.95f; + public override object Create(ActorInitializer init) { return new PlayerColorShift(this); } } @@ -50,8 +53,11 @@ public PlayerColorShift(PlayerColorShiftInfo info) public void LoadPlayerPalettes(WorldRenderer wr, string playerName, Color color, bool replaceExisting) { - var (_, h, s, _) = color.ToAhsv(); - wr.SetPaletteColorShift(info.BasePalette + playerName, h - info.ReferenceHue, s - info.ReferenceSaturation, info.MinHue, info.MaxHue); + var (r, g, b) = color.ToLinear(); + var (h, s, v) = Color.RgbToHsv(r, g, b); + wr.SetPaletteColorShift(info.BasePalette + playerName, + h - info.ReferenceHue, s - info.ReferenceSaturation, v / info.ReferenceValue, + info.MinHue, info.MaxHue); } } } diff --git a/OpenRA.Mods.Mobius/Traits/World/RemasterTerrainRenderer.cs b/OpenRA.Mods.Mobius/Traits/World/RemasterTerrainRenderer.cs index 64fe823..7b2acb8 100644 --- a/OpenRA.Mods.Mobius/Traits/World/RemasterTerrainRenderer.cs +++ b/OpenRA.Mods.Mobius/Traits/World/RemasterTerrainRenderer.cs @@ -38,7 +38,7 @@ bool ITiledTerrainRendererInfo.ValidateTileSprites(ITemplatedTerrainInfo terrain { if (!tileCache.HasTileSprite(new TerrainTile(t.Key, (byte)kv.Key), i)) { - onError("\tTemplate `{0}` tile {1} references sprite `{2}` that does not exist.".F(t.Key, kv.Key, templateInfo.RemasteredFilenames[i])); + onError($"\tTemplate `{t.Key}` tile {kv.Key} references sprite `{templateInfo.RemasteredFilenames[i]}` that does not exist."); failed = true; } } @@ -147,7 +147,7 @@ Rectangle ITiledTerrainRenderer.TemplateBounds(TerrainTemplateInfo template) { for (var x = 0; x < template.Size.X; x++) { - var tile = new TerrainTile(template.Id, (byte)(i++)); + var tile = new TerrainTile(template.Id, (byte)i++); if (!terrainInfo.TryGetTileInfo(tile, out var tileInfo)) continue; @@ -165,12 +165,12 @@ Rectangle ITiledTerrainRenderer.TemplateBounds(TerrainTemplateInfo template) } } - return templateRect.HasValue ? templateRect.Value : Rectangle.Empty; + return templateRect ?? Rectangle.Empty; } IEnumerable ITiledTerrainRenderer.RenderUIPreview(WorldRenderer wr, TerrainTemplateInfo t, int2 origin, float scale) { - if (!(t is RemasterTerrainTemplateInfo template)) + if (t is not RemasterTerrainTemplateInfo template) yield break; var ts = map.Grid.TileSize; @@ -202,8 +202,7 @@ IEnumerable ITiledTerrainRenderer.RenderUIPreview(WorldRenderer wr, IEnumerable ITiledTerrainRenderer.RenderPreview(WorldRenderer wr, TerrainTemplateInfo t, WPos origin) { - var template = t as RemasterTerrainTemplateInfo; - if (template == null) + if (t is not RemasterTerrainTemplateInfo template) yield break; var i = 0; diff --git a/OpenRA.Mods.Mobius/UtilityCommands/RemasterTilesetConverter.cs b/OpenRA.Mods.Mobius/UtilityCommands/RemasterTilesetConverter.cs index 7aefb5f..15f3f13 100644 --- a/OpenRA.Mods.Mobius/UtilityCommands/RemasterTilesetConverter.cs +++ b/OpenRA.Mods.Mobius/UtilityCommands/RemasterTilesetConverter.cs @@ -43,7 +43,7 @@ void IUtilityCommand.Run(Utility utility, string[] args) var config = new MegV3Loader.MegFile(ffs, args[2]); // e.g. TD_TERRAIN_TEMPERATE.XML, TD_TERRAIN_DESERT.XML, TD_TERRAIN_WINTER.XML - mapping.Load(config.GetStream("DATA\\XML\\TILESETS\\{0}".F(args[3]))); + mapping.Load(config.GetStream($"DATA\\XML\\TILESETS\\{args[3]}")); } var rootTexturePath = mapping.SelectSingleNode("//RootTexturePath").InnerText.ToUpperInvariant(); @@ -52,7 +52,7 @@ void IUtilityCommand.Run(Utility utility, string[] args) var legacy = template.LastChildMatching("Images").Value.Value; var code = Path.GetFileNameWithoutExtension(legacy).ToUpperInvariant(); - var tileNodes = mapping.DocumentElement.SelectNodes("//Tile[Key/Name = '{0}']".F(code)); + var tileNodes = mapping.DocumentElement.SelectNodes($"//Tile[Key/Name = '{code}']"); if (tileNodes == null) { @@ -69,7 +69,10 @@ void IUtilityCommand.Run(Utility utility, string[] args) var index = tileNode.SelectSingleNode("Key/Shape").InnerText; var frames = new List(); foreach (var f in tileNode.SelectNodes("Value/Frames/Frame")) - frames.Add("DATA\\ART\\TEXTURES\\SRGB\\{0}\\{1}".F(rootTexturePath, Path.ChangeExtension(((XmlNode)f).InnerText, ".DDS").ToUpperInvariant())); + { + var path = Path.ChangeExtension(((XmlNode)f).InnerText, ".DDS").ToUpperInvariant(); + frames.Add($"DATA\\ART\\TEXTURES\\SRGB\\{rootTexturePath}\\{path}"); + } imageNode.AddNode(index, FieldSaver.FormatValue(frames)); } diff --git a/mod.config b/mod.config index 87bf4a5..60d7501 100644 --- a/mod.config +++ b/mod.config @@ -9,7 +9,7 @@ MOD_ID="cnc" # The OpenRA engine version to use for this project. -ENGINE_VERSION="7c31e8c235" +ENGINE_VERSION="d2bf6d61cf" ############################################################################## # Packaging diff --git a/mods/cnc/languages/difficulties/en.ftl b/mods/cnc/languages/difficulties/en.ftl new file mode 100644 index 0000000..90ad411 --- /dev/null +++ b/mods/cnc/languages/difficulties/en.ftl @@ -0,0 +1,7 @@ +dropdown-difficulty = + .label = Difficulty + .description = The difficulty of the mission + +options-difficulty = + .easy = Easy + .hard = Hard diff --git a/mods/cnc/languages/lua/en.ftl b/mods/cnc/languages/lua/en.ftl new file mode 100644 index 0000000..4d02df4 --- /dev/null +++ b/mods/cnc/languages/lua/en.ftl @@ -0,0 +1,162 @@ +## campaign +objective-failed = Objective failed +objective-completed = Objective completed + +primary = Primary +secondary = Secondary + +new-primary-objective = New primary objective +new-secondary-objective = New secondary objective + +# cnc64gdi01 +destroy-obelisk-sams = Destroy the SAM sites protecting the Obelisk. +destroy-obelisk = Destroy the Obelisk. +destroy-biotech = Destroy the biotech facility. + +## gdi03, gdi05abc, gdi07 gdi08ab, gdi09 +destroy-sams = Destroy the SAM sites to receive air support. + +## funpark01 +investigate-village = Investigate the nearby village for reports of + strange activity. +reach-village = Reach the village. +kill-creatures = Kill all creatures in the area. + +## gdi01 +establish-beachhead = Establish a beachhead. + +## gdi01, gdi02, gdi03, gdi04c, gdi05abc, twist-of-fate +eliminate-nod = Eliminate all Nod forces in the area. + +## gdi02, nod09 +capture-refinery = Capture the Tiberium refinery. + +## gdi04ab +battlefield-control = Battlefield Control +apcs-left = APC squads in reserve: { $apcs } +retrieve-rods = Retrieve the crate with the stolen rods. +eliminate-reinforcements = Eliminate { $kills } Nod units for reinforcements. + +## gdi04c +defend-bialystok = Defend the town of BiaƂystok. + +## gdi05abc +find-gdi-base = Find the GDI base. + +## gdi06 +destroy-nod-building = Destroy the Nod ********. + +## gdi07 +destroy-nod = Destroy remaining Nod structures and units. +construct-base = Construct all available buildings. + +## gdi08a +repair-base-vehicles = Repair GDI base and vehicles. + +## gdi08a, gdi09 +destroy-nod-force = Destroy the Nod strike force. + +## gdi08b +protect-mobius = Protect Dr. Mobius. +protect-hospital = Protect the Hospital. +keep-civilians-alive = Keep at least { $civilians } out of 14 Civilians alive. +destroy-nod-bases = Destroy the Nod bases. + +## gdi09 +destroy-nod-bunkers = Destroy the Nod bunkers to allow Carter's + convoy to pass through safely. +convoy-passed-partly = Part of Carter's convoy passed through! + +## nod01 +destroy-gdi-troops-area = Destroy all GDI troops in the area. +kill-nikoomba = Kill Nikoomba. +destroy-village = Destroy the village. + +## nod02a +destroy-gdi-units = Destroy all GDI units. + +## nod02ab +build-base = Build a base. + +## nod02b, nod05 +destroy-gdi-base = Destroy the GDI base. + +## nod03ab +capture-prison = Capture the prison. +destroy-gdi-forces = Destroy all GDI forces. + +## nod04a +destroy-village-kill-civilians = Destroy the village and kill all civilians. +kill-gdi-units = Kill all GDI units in the area. + +## nod04b +protect-nod-supporters = Protect the civilians that support Nod. +kill-gdi-supporters = Kill all civilian GDI supporters. + +## nod05, nod07ab, nod09 +build-sams = Build { $sams } SAMs to fend off the GDI bombers. + +## nod06abc +steal-nuclear-detonator = Steal the GDI nuclear detonator. +move-to-evacuation-point = Move to the evacuation point. + +## nod06ab +destroy-gdi-supporter-houses = Destroy the houses of the GDI supporters + in the village. + +## nod06c +infiltrate-barracks-factory-conyard = Infiltrate the barracks, weapon factory and + the construction yard. + +## nod07a +find-nod-base = Find the Nod base. + +## nod07ab, nod08ab, nod09, eviction-notice +eliminate-gdi-forces = Eliminate all GDI forces in the area. + +## nod07c +capture-gdi-helipad = Capture the GDI helipad. +dont-capture-or-destroy = Don't capture or destroy any other + GDI main building. +orca-wreak-havoc = Use the GDI orca to wreak havoc at the village. +distract-guards = Distract the guards by attacking the + main entrance with your vehicles. + +## nod08ab +capture-gdi-outpost = Capture the GDI outpost. + +## nod08ab, nod09 +locate-nod-base = Locate the Nod base. + +## nod09 +secure-first-landing-zone = Secure the first landing zone. +secure-second-landing-zone = Secure the second landing zone. + +## nod10a +kill-gdi-scientist = Kill the GDI scientist. +destroy-tech-center = Destroy the GDI R&D center. + +## nod10b +destroy-capture-warfactory = Destroy or capture the Weapons Factory. +destroy-mammoth-tanks = Destroy the Mammoth tanks in the R&D base. +keep-commando-alive = Keep your Commando alive. + +## eviction-notice +take-civilians-money-crates = Find all the civilians' money. + They won't need it anymore. +quickly-destroy-ion-cannon = Disable GDI Ion Cannon before + it fires two times. +nod-soldier = Nod Soldier +civilians-runs = Hey, those civilians... where are they going? +destroy-ion-cannon-advise = The GDI are preparing their ion cannon. Don't let them get used to it. +village-destruction-warning = Be careful, commander. The GDI won't stand still while we burn the entire village. + +## twist-of-fate +clear-path = Repel the ambush and clear the way + for our MCV. +recover-old-base = Capture the Construction Yard in our recon + post to regain control. +air-strikes-intel-report = Nod airstrikes are being directed by a Communications Center, located northwest. We may gain useful information from its capture. +capture-nod-communications-center = Capture the Nod Communications Center to + the northwest. +communications-center-captured-sams-located = Our engineers located Nod SAM sites. They also disarmed an unusual trap on the Construction Yard to the south. diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 12a0088..b60b5d4 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -145,10 +145,11 @@ ChromeLayout: common|chrome/ingame-transients.yaml base|chrome/ingame-menu.yaml base|chrome/ingame-debug.yaml + common|chrome/ingame-debug-hpf.yaml base|chrome/ingame-infochat.yaml base|chrome/ingame-info.yaml base|chrome/ingame-infobriefing.yaml - base|chrome/ingame-infoscripterror.yaml + common|chrome/ingame-infoscripterror.yaml base|chrome/ingame-infoobjectives.yaml base|chrome/ingame-infostats.yaml base|chrome/ingame-info-lobby-options.yaml @@ -170,6 +171,8 @@ ChromeLayout: Translations: common|languages/en.ftl + common|languages/rules/en.ftl + base|languages/rules/en.ftl Voices: cnc|audio/voices.yaml @@ -268,12 +271,6 @@ TerrainFormat: RemasterTerrain SpriteSequenceFormat: RemasterSpriteSequence BgraSheetSize: 8192 IndexedSheetSize: 512 - TilesetExtensions: - TEMPERAT: .tem - WINTER: .win - SNOW: .sno - DESERT: .des - JUNGLE: .jun ModelSequenceFormat: PlaceholderModelSequence @@ -286,27 +283,27 @@ GameSpeeds: DefaultSpeed: default Speeds: slowest: - Name: slowest + Name: options-game-speed.slowest Timestep: 80 OrderLatency: 2 slower: - Name: slower + Name: options-game-speed.slower Timestep: 50 OrderLatency: 3 default: - Name: normal + Name: options-game-speed.normal Timestep: 40 OrderLatency: 3 fast: - Name: fast + Name: options-game-speed.fast Timestep: 35 OrderLatency: 4 faster: - Name: faster + Name: options-game-speed.faster Timestep: 30 OrderLatency: 4 fastest: - Name: fastest + Name: options-game-speed.fastest Timestep: 20 OrderLatency: 6 diff --git a/packaging/windows/buildpackage.nsi b/packaging/windows/buildpackage.nsi index e337a22..c254896 100644 --- a/packaging/windows/buildpackage.nsi +++ b/packaging/windows/buildpackage.nsi @@ -111,9 +111,6 @@ Section "Game" GAME "$OUTDIR\${PACKAGING_WINDOWS_LAUNCHER_NAME}.exe" "" "" "" "" !insertmacro MUI_STARTMENU_WRITE_END - SetOutPath "$INSTDIR\lua" - File "${SRCDIR}\lua\*.lua" - SetOutPath "$INSTDIR\glsl" File "${SRCDIR}\glsl\*.frag" File "${SRCDIR}\glsl\*.vert" @@ -162,7 +159,6 @@ Function ${UN}Clean RMDir /r $INSTDIR\mods RMDir /r $INSTDIR\maps RMDir /r $INSTDIR\glsl - RMDir /r $INSTDIR\lua Delete $INSTDIR\*.exe Delete $INSTDIR\*.dll Delete $INSTDIR\*.ico