diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1af917f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,614 @@ +root = true +# Remove the line below if you want to inherit .editorconfig settings from higher directories + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:none +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:none + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = true:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:silent + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion + +# Code-block preferences +csharp_prefer_braces = when_multiline:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = accessors,anonymous_methods,control_blocks,events,indexers,local_functions,methods,properties,types +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_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_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.struct_should_be_begin_with_s.severity = warning +dotnet_naming_rule.struct_should_be_begin_with_s.symbols = struct +dotnet_naming_rule.struct_should_be_begin_with_s.style = begin_with_s + +dotnet_naming_rule.property_should_be_pascal_case.severity = warning +dotnet_naming_rule.property_should_be_pascal_case.symbols = property +dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.constant_field_should_be_begins_with_c_.severity = warning +dotnet_naming_rule.constant_field_should_be_begins_with_c_.symbols = constant_field +dotnet_naming_rule.constant_field_should_be_begins_with_c_.style = begins_with_c_ + +dotnet_naming_rule.static_field_should_be_starts_with_s_.severity = warning +dotnet_naming_rule.static_field_should_be_starts_with_s_.symbols = static_field +dotnet_naming_rule.static_field_should_be_starts_with_s_.style = starts_with_s_ + +dotnet_naming_rule.private_or_internal_static_field_should_be_starts_with_s_.severity = warning +dotnet_naming_rule.private_or_internal_static_field_should_be_starts_with_s_.symbols = private_or_internal_static_field +dotnet_naming_rule.private_or_internal_static_field_should_be_starts_with_s_.style = starts_with_s_ + +dotnet_naming_rule.public_or_protected_field_should_be_starts_with_m_.severity = warning +dotnet_naming_rule.public_or_protected_field_should_be_starts_with_m_.symbols = public_or_protected_field +dotnet_naming_rule.public_or_protected_field_should_be_starts_with_m_.style = starts_with_m_ + +dotnet_naming_rule.private_or_internal_field_should_be_starts_with_m_.severity = warning +dotnet_naming_rule.private_or_internal_field_should_be_starts_with_m_.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_starts_with_m_.style = starts_with_m_ + +dotnet_naming_rule.event_should_be_end_with_event.severity = warning +dotnet_naming_rule.event_should_be_end_with_event.symbols = event +dotnet_naming_rule.event_should_be_end_with_event.style = end_with_event + +dotnet_naming_rule.delegate_should_be_end_with_delegate.severity = warning +dotnet_naming_rule.delegate_should_be_end_with_delegate.symbols = delegate +dotnet_naming_rule.delegate_should_be_end_with_delegate.style = end_with_delegate + +dotnet_naming_rule.parameter_should_be_camelcase.severity = warning +dotnet_naming_rule.parameter_should_be_camelcase.symbols = parameter +dotnet_naming_rule.parameter_should_be_camelcase.style = camelcase + +dotnet_naming_rule.local_should_be_camelcase.severity = warning +dotnet_naming_rule.local_should_be_camelcase.symbols = local +dotnet_naming_rule.local_should_be_camelcase.style = camelcase + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.struct.applicable_kinds = struct +dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.struct.required_modifiers = + +dotnet_naming_symbols.delegate.applicable_kinds = delegate +dotnet_naming_symbols.delegate.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.delegate.required_modifiers = + +dotnet_naming_symbols.event.applicable_kinds = event +dotnet_naming_symbols.event.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.event.required_modifiers = + +dotnet_naming_symbols.property.applicable_kinds = property +dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.property.required_modifiers = + +dotnet_naming_symbols.public_or_protected_field.applicable_kinds = field +dotnet_naming_symbols.public_or_protected_field.applicable_accessibilities = public, protected +dotnet_naming_symbols.public_or_protected_field.required_modifiers = + +dotnet_naming_symbols.static_field.applicable_kinds = field +dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.static_field.required_modifiers = static + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.parameter.applicable_kinds = parameter +dotnet_naming_symbols.parameter.applicable_accessibilities = * +dotnet_naming_symbols.parameter.required_modifiers = + +dotnet_naming_symbols.local.applicable_kinds = local +dotnet_naming_symbols.local.applicable_accessibilities = local +dotnet_naming_symbols.local.required_modifiers = + +dotnet_naming_symbols.constant_field.applicable_kinds = field +dotnet_naming_symbols.constant_field.applicable_accessibilities = * +dotnet_naming_symbols.constant_field.required_modifiers = const + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.begin_with_s.required_prefix = S +dotnet_naming_style.begin_with_s.required_suffix = +dotnet_naming_style.begin_with_s.word_separator = +dotnet_naming_style.begin_with_s.capitalization = pascal_case + +dotnet_naming_style.starts_with_m_.required_prefix = m_ +dotnet_naming_style.starts_with_m_.required_suffix = +dotnet_naming_style.starts_with_m_.word_separator = +dotnet_naming_style.starts_with_m_.capitalization = pascal_case + +dotnet_naming_style.starts_with_s_.required_prefix = s_ +dotnet_naming_style.starts_with_s_.required_suffix = +dotnet_naming_style.starts_with_s_.word_separator = +dotnet_naming_style.starts_with_s_.capitalization = pascal_case + +dotnet_naming_style.end_with_delegate.required_prefix = +dotnet_naming_style.end_with_delegate.required_suffix = Delegate +dotnet_naming_style.end_with_delegate.word_separator = +dotnet_naming_style.end_with_delegate.capitalization = pascal_case + +dotnet_naming_style.end_with_event.required_prefix = +dotnet_naming_style.end_with_event.required_suffix = Event +dotnet_naming_style.end_with_event.word_separator = +dotnet_naming_style.end_with_event.capitalization = pascal_case + +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.capitalization = camel_case + +dotnet_naming_style.begins_with_c_.required_prefix = c_ +dotnet_naming_style.begins_with_c_.required_suffix = +dotnet_naming_style.begins_with_c_.word_separator = +dotnet_naming_style.begins_with_c_.capitalization = pascal_case + +# ReSharper properties +resharper_align_linq_query = true +resharper_align_multiline_argument = true +resharper_align_multiline_calls_chain = true +resharper_align_multiline_extends_list = true +resharper_align_multiline_for_stmt = true +resharper_align_multline_type_parameter_constrains = true +resharper_align_multline_type_parameter_list = true +resharper_align_tuple_components = true +resharper_allow_far_alignment = false +resharper_apply_auto_detected_rules = false +resharper_blank_lines_after_control_transfer_statements = 1 +resharper_blank_lines_after_file_scoped_namespace_directive = 1 +resharper_blank_lines_around_single_line_type = 0 +resharper_blank_lines_before_multiline_statements = 0 +resharper_braces_for_fixed = required +resharper_braces_for_lock = required +resharper_braces_for_using = required +resharper_csharp_align_multiline_parameter = true +resharper_csharp_align_multiple_declaration = true +resharper_csharp_allow_far_alignment = true +resharper_csharp_blank_lines_around_field = 0 +resharper_csharp_extra_spaces = remove_all +resharper_csharp_insert_final_newline = false +resharper_csharp_keep_blank_lines_in_code = 1 +resharper_csharp_keep_blank_lines_in_declarations = 1 +resharper_csharp_max_line_length = 500 +resharper_csharp_naming_rule.constants = c_ + AaBb +resharper_csharp_naming_rule.event = AaBb +resharper_csharp_naming_rule.interfaces = I + AaBb +resharper_csharp_naming_rule.local_constants = aaBb +resharper_csharp_naming_rule.method = AaBb +resharper_csharp_naming_rule.private_constants = c_ + AaBb +resharper_csharp_naming_rule.private_instance_fields = m_ + AaBb +resharper_csharp_naming_rule.private_static_fields = s_ + AaBb +resharper_csharp_naming_rule.private_static_readonly = s_ + AaBb +resharper_csharp_naming_rule.property = AaBb +resharper_csharp_naming_rule.public_fields = m_ + AaBb +resharper_csharp_naming_rule.static_readonly = s_ + AaBb +resharper_csharp_naming_rule.types_and_namespaces = AaBb +resharper_csharp_outdent_commas = true +resharper_csharp_outdent_dots = false +resharper_csharp_stick_comment = false +resharper_csharp_use_indent_from_vs = true +resharper_csharp_wrap_extends_list_style = chop_if_long +resharper_csharp_wrap_lines = false +resharper_csharp_wrap_parameters_style = chop_if_long +resharper_csharp_wrap_ternary_expr_style = wrap_if_long +resharper_csharp_wrap_chained_method_calls = chop_if_long +resharper_extra_spaces = remove_all +resharper_for_built_in_types = use_var +resharper_indent_preprocessor_region = no_indent +resharper_instance_members_qualify_declared_in = +resharper_int_align_switch_expressions = false +resharper_int_align_switch_sections = false +resharper_keep_existing_arrangement = true +resharper_keep_existing_attribute_arrangement = true +resharper_keep_existing_switch_expression_arrangement = true +resharper_max_attribute_length_for_same_line = 0 +resharper_max_initializer_elements_on_line = 1 +resharper_nested_ternary_style = compact +resharper_object_creation_when_type_not_evident = target_typed +resharper_outdent_binary_ops = true +resharper_place_constructor_initializer_on_same_line = false +resharper_place_method_attribute_on_same_line = if_owner_is_single_line +resharper_place_simple_case_statement_on_same_line = true +resharper_place_simple_embedded_statement_on_same_line = false +resharper_place_simple_initializer_on_single_line = true +resharper_place_simple_property_pattern_on_single_line = true +resharper_place_type_attribute_on_same_line = if_owner_is_single_line +resharper_use_heuristics_for_body_style = true +resharper_use_indent_from_vs = true +resharper_space_before_new_parentheses = false +resharper_use_roslyn_logic_for_evident_types = true + +# IDE0090: Use 'new(...)' +dotnet_diagnostic.ide0090.severity = silent +dotnet_diagnostic.ide0110.severity = suggestion +dotnet_diagnostic.ide0058.severity = suggestion +dotnet_diagnostic.ide0059.severity = suggestion +csharp_style_namespace_declarations = file_scoped:none +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_prefer_extended_property_pattern = true:suggestion + +# Microsoft .NET properties +dotnet_naming_rule.constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.constants_rule.severity = none +dotnet_naming_rule.constants_rule.style = begins_with_c_ +dotnet_naming_rule.constants_rule.symbols = constants_symbols +dotnet_naming_rule.event_rule.import_to_resharper = as_predefined +dotnet_naming_rule.event_rule.severity = none +dotnet_naming_rule.event_rule.style = pascal_case +dotnet_naming_rule.event_rule.symbols = event_symbols +dotnet_naming_rule.interfaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.interfaces_rule.severity = none +dotnet_naming_rule.interfaces_rule.style = begins_with_i +dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols +dotnet_naming_rule.local_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_constants_rule.severity = none +dotnet_naming_rule.local_constants_rule.style = camelcase +dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols +dotnet_naming_rule.local_should_be_camelcase.import_to_resharper = as_predefined +dotnet_naming_rule.method_rule.import_to_resharper = as_predefined +dotnet_naming_rule.method_rule.severity = none +dotnet_naming_rule.method_rule.style = pascal_case +dotnet_naming_rule.method_rule.symbols = method_symbols +dotnet_naming_rule.non_field_members_should_be_pascal_case.import_to_resharper = True +dotnet_naming_rule.non_field_members_should_be_pascal_case.resharper_description = non_field_members_should_be_pascal_case +dotnet_naming_rule.non_field_members_should_be_pascal_case.resharper_guid = 44ab0b2d-34cc-42db-a691-0c646821e2e4 +dotnet_naming_rule.parameter_should_be_camelcase.import_to_resharper = as_predefined +dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_constants_rule.severity = none +dotnet_naming_rule.private_constants_rule.style = begins_with_c_ +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_instance_fields_rule.severity = none +dotnet_naming_rule.private_instance_fields_rule.style = starts_with_m_ +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_fields_rule.severity = none +dotnet_naming_rule.private_static_fields_rule.style = starts_with_s_ +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_readonly_rule.severity = none +dotnet_naming_rule.private_static_readonly_rule.style = starts_with_s_ +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.property_rule.import_to_resharper = as_predefined +dotnet_naming_rule.property_rule.severity = none +dotnet_naming_rule.property_rule.style = pascal_case +dotnet_naming_rule.property_rule.symbols = property_symbols +dotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.public_fields_rule.severity = none +dotnet_naming_rule.public_fields_rule.style = starts_with_m_ +dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols +dotnet_naming_rule.static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.static_readonly_rule.severity = none +dotnet_naming_rule.static_readonly_rule.style = starts_with_s_ +dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols +dotnet_naming_rule.types_and_namespaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.types_and_namespaces_rule.severity = none +dotnet_naming_rule.types_and_namespaces_rule.style = pascal_case +dotnet_naming_rule.types_and_namespaces_rule.symbols = types_and_namespaces_symbols +dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.constants_symbols.applicable_kinds = field +dotnet_naming_symbols.constants_symbols.required_modifiers = const +dotnet_naming_symbols.event_symbols.applicable_accessibilities = * +dotnet_naming_symbols.event_symbols.applicable_kinds = event +dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface +dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.required_modifiers = const +dotnet_naming_symbols.method_symbols.applicable_accessibilities = * +dotnet_naming_symbols.method_symbols.applicable_kinds = method +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.property_symbols.applicable_accessibilities = * +dotnet_naming_symbols.property_symbols.applicable_kinds = property +dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate + +# ReSharper inspection severities +resharper_arrange_accessor_owner_body_highlighting = suggestion +resharper_arrange_default_value_when_type_evident_highlighting = hint +resharper_arrange_object_creation_when_type_evident_highlighting = hint +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_comment_typo_highlighting = hint +resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = none +resharper_identifier_typo_highlighting = hint +resharper_inconsistent_naming_highlighting = none +resharper_json_validation_failed_highlighting = warning +resharper_member_can_be_private_global_highlighting = hint +resharper_member_can_be_private_local_highlighting = hint +resharper_member_can_be_protected_global_highlighting = hint +resharper_member_can_be_protected_local_highlighting = hint +resharper_redundant_base_qualifier_highlighting = warning +resharper_remove_redundant_braces_highlighting = suggestion +resharper_string_literal_typo_highlighting = hint +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_unused_auto_property_accessor_global_highlighting = hint +resharper_unused_auto_property_accessor_local_highlighting = hint +resharper_unused_member_global_highlighting = hint +csharp_style_prefer_method_group_conversion = true:suggestion +csharp_style_prefer_top_level_statements = true:silent +dotnet_diagnostic.VSTHRD001.severity = none +dotnet_diagnostic.VSTHRD105.severity = silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +dotnet_diagnostic.VSTHRD012.severity = none +dotnet_diagnostic.VSTHRD003.severity = none + +[*.{cs,vb}] +dotnet_diagnostic.ca1822.severity = silent +dotnet_diagnostic.ca1305.severity = suggestion +dotnet_diagnostic.IDE0079.severity = silent +dotnet_diagnostic.IDE0270.severity = silent +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_diagnostic.ca2215.severity = warning +dotnet_diagnostic.ca1001.severity = suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent + +[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 + +[*.{appxmanifest,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cshtml,csproj,cu,cuh,cxx,dbml,discomap,dtd,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,jsproj,lsproj,mpp,mq4,mq5,mqh,njsproj,nuspec,proj,props,proto,razor,resw,resx,StyleCop,targets,tasks,tpp,usf,ush,vbproj,xml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 + +[*.{asax,ascx,aspx,axaml,cs,css,js,jsx,master,paml,skin,ts,tsx,vb,xaml,xamlx,xoml}] +indent_style = space +indent_size = 4 +tab_width = 4 +dotnet_diagnostic.CA1816.severity = silent +dotnet_diagnostic.HAA0501.severity = silent +dotnet_diagnostic.HAA0502.severity = silent +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_diagnostic.RS2008.severity = none + +[*.{json,resjson}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/AGENTS.linux.md b/AGENTS.linux.md index 33a8d7b..59a55ea 100644 --- a/AGENTS.linux.md +++ b/AGENTS.linux.md @@ -1 +1,3 @@ -# Linux-specific instructions +# Linux-specific instructions + +- After every iteration, run `dotnet jb cleanupcode --build=False '$file1' '$file2' ...` for every C# file you touched. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index e384bb1..15a0c6b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# Platform and documentation +# Platform and documentation If this is a linux environment, read `AGENTS.linux.md`. If this is a windows environment, read `AGENTS.windows.md`. @@ -13,7 +13,6 @@ Also see the other related technical documentation in the docs folder. - When an task is finished, perform a code review to evaluate if the change is clean and maintainable with high software engineering standards. Iterate on the code and repeat the review process until satisfied. - If there's documnentation present, always keep it updated. - After every iteration, evaluate if the test coverage would fall below 100%, and write tests if necessary. -- After every iteration, run `dotnet jb cleanupcode --build=False '$file1' '$file2' ...` for every C# file you touched. ### Git diff --git a/AGENTS.windows.md b/AGENTS.windows.md index 6d72d22..4b43674 100644 --- a/AGENTS.windows.md +++ b/AGENTS.windows.md @@ -1 +1,4 @@ -# Windows-specific instructions +# Windows-specific instructions + +- After the implementation is finished, run `python D:\Code\crlf.py $file1 $file2 ...` for changed files you recognize, in order to normalize all line endings of all touched files to CRLF. +- After every iteration, run `jb cleanupcode --build=False '$file1' '$file2' ...` for every C# file you touched. \ No newline at end of file diff --git a/CODESTYLE.md b/CODESTYLE.md new file mode 100644 index 0000000..5035862 --- /dev/null +++ b/CODESTYLE.md @@ -0,0 +1,56 @@ +# Code Style + +This repository follows the local `.editorconfig` and the style visible in the current local changes. Use these notes when creating new code. + +## Naming + +- Use PascalCase for namespaces, types, methods, properties, enum members, and non-field members. +- Prefix enum type names with `E`, for example `ECellKind`, `EPipeMedium`, `EFailureKind`, and `EEditorTool`. +- Prefix struct type names with `S` when creating new structs. +- Prefix interfaces with `I`. +- Use camelCase for parameters and local variables. +- Prefix private instance fields with `m_` and keep the remainder PascalCase, for example `m_Level` and `m_SelectedTool`. +- Prefix private static fields and static readonly fields with `s_`. +- Prefix constants with `c_`. +- Avoid `this.` unless it is needed for clarity or disambiguation. + +## Files And Types + +- Use file-scoped namespaces. +- Keep one reusable top-level class per file, with the file name matching the class name. +- If a helper type is used only by one class, prefer nesting it inside that class. +- Keep small, cohesive files and extract shared helpers instead of duplicating logic. + +## Braces And Blocks + +- Use braces for multi-line bodies. +- Omit braces for simple single-line embedded statements when readability stays clear. +- Nested control flow with multi-line bodies should use braces at every multi-line level. +- Keep opening braces on the next line for types, methods, properties, accessors, and control blocks. +- Compact object initializers, switch expressions, and `with` expressions may keep the opening brace on the same line when cleanup formats them that way. + +## Blank Lines + +- Use a blank line to separate members. +- Use a blank line after control-flow transfer clauses such as `return`, `continue`, `break`, and `throw` when more code follows in the same scope. +- Avoid extra blank lines inside short methods and between tightly related statements. +- Keep at most one blank line in code and declarations. + +## Expressions And Formatting + +- Prefer `var` when the type is apparent or not useful to repeat; use explicit built-in types such as `int`, `bool`, and `string`. +- Prefer target-typed `new()` when the type is evident. +- Prefer object and collection initializers, including collection expressions such as `[".json"]`. +- Prefer pattern matching for combined checks, for example `cell is { HasPipe: true, Pressure: > 7 }`. +- Prefer switch expressions for simple value selection. +- Prefer expression-bodied properties and accessors when they remain simple. +- Keep simple object initializers and property patterns on one line when they are short and readable. +- Keep long boolean expressions and interpolated status strings readable without introducing unnecessary blank lines. +- Keep using directives outside namespaces. + +## Line Endings And Encoding + +- Normalize repository files with `python D:\Code\crlf.py ...`. +- Use CRLF line endings. +- Files normalized with the repository script are UTF-8 with BOM. +- Do not require a final newline unless the existing file or tooling specifically needs one. diff --git a/README.md b/README.md index 80ec49b..e2fada6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Reactor Maintenance +# Reactor Maintenance C# WinUI 3 + Win2D level editor for the deterministic grid simulation described in `design.md`. diff --git a/ReactorMaintenance.slnx b/ReactorMaintenance.slnx index c4c88ca..64f4c3f 100644 --- a/ReactorMaintenance.slnx +++ b/ReactorMaintenance.slnx @@ -1,4 +1,4 @@ - + diff --git a/design.md b/design.md index a88a157..f8b6e6d 100644 --- a/design.md +++ b/design.md @@ -1,4 +1,4 @@ -# Game Design: Reactor Maintenance +# Game Design: Reactor Maintenance ## Core Concept diff --git a/src/ReactorMaintenance.Simulation/LevelEditor.cs b/src/ReactorMaintenance.Simulation/LevelEditor.cs index c40adc2..0e88061 100644 --- a/src/ReactorMaintenance.Simulation/LevelEditor.cs +++ b/src/ReactorMaintenance.Simulation/LevelEditor.cs @@ -1,6 +1,6 @@ -namespace ReactorMaintenance.Simulation; +namespace ReactorMaintenance.Simulation; -public enum EditorTool +public enum EEditorTool { Floor, Wall, @@ -22,90 +22,76 @@ public enum EditorTool public static class LevelEditor { - public static LevelState Apply(LevelState level, GridPosition position, EditorTool tool) + public static LevelState Apply(LevelState level, GridPosition position, EEditorTool tool) { if (!level.InBounds(position)) return level; - if (tool == EditorTool.Robot) + if (tool == EEditorTool.Robot) return level.GetCell(position).IsWalkable ? level with { Robot = position } : level; var cell = level.GetCell(position); - cell = tool switch - { - EditorTool.Floor => cell with { Kind = CellKind.Floor }, - EditorTool.Wall => cell with - { - Kind = CellKind.Wall, - Pipe = PipeMedium.None, + cell = tool switch { + EEditorTool.Floor => cell with { Kind = ECellKind.Floor }, + EEditorTool.Wall => cell with { + Kind = ECellKind.Wall, + Pipe = EPipeMedium.None, Powered = false }, - EditorTool.Reactor => cell with { Kind = CellKind.Reactor }, - EditorTool.CoolingPump => cell with - { - Kind = CellKind.CoolingPump, + EEditorTool.Reactor => cell with { Kind = ECellKind.Reactor }, + EEditorTool.CoolingPump => cell with { + Kind = ECellKind.CoolingPump, Powered = true }, - EditorTool.Generator => cell with - { - Kind = CellKind.Generator, + EEditorTool.Generator => cell with { + Kind = ECellKind.Generator, Powered = true }, - EditorTool.PressureRegulator => cell with { Kind = CellKind.PressureRegulator }, - EditorTool.DiagnosticTerminal => cell with - { - Kind = CellKind.DiagnosticTerminal, + EEditorTool.PressureRegulator => cell with { Kind = ECellKind.PressureRegulator }, + EEditorTool.DiagnosticTerminal => cell with { + Kind = ECellKind.DiagnosticTerminal, Powered = true }, - EditorTool.ControlTerminal => cell with - { - Kind = CellKind.ControlTerminal, + EEditorTool.ControlTerminal => cell with { + Kind = ECellKind.ControlTerminal, Powered = true }, - EditorTool.CoolantPipe => cell with - { - Pipe = PipeMedium.Coolant, + EEditorTool.CoolantPipe => cell with { + Pipe = EPipeMedium.Coolant, Flow = 4, Pressure = 4, Integrity = Math.Max(cell.Integrity, 8), PipeOpen = true }, - EditorTool.FuelPipe => cell with - { - Pipe = PipeMedium.Fuel, + EEditorTool.FuelPipe => cell with { + Pipe = EPipeMedium.Fuel, Flow = 4, Pressure = 4, Integrity = Math.Max(cell.Integrity, 8), PipeOpen = true }, - EditorTool.PressurePipe => cell with - { - Pipe = PipeMedium.Pressure, + EEditorTool.PressurePipe => cell with { + Pipe = EPipeMedium.Pressure, Flow = 5, Pressure = 6, Integrity = Math.Max(cell.Integrity, 8), PipeOpen = true }, - EditorTool.Leak => cell with - { + EEditorTool.Leak => cell with { LeakRate = Math.Max(1, cell.LeakRate), Integrity = Math.Min(cell.Integrity, 4) }, - EditorTool.Repair => cell with - { + EEditorTool.Repair => cell with { LeakRate = 0, Integrity = 10, - Hazards = cell.Hazards with - { + Hazards = cell.Hazards with { Fire = false, ElectricalCharge = 0 } }, - EditorTool.Heat => cell with { Hazards = cell.Hazards with { Heat = Rules.Clamp(cell.Hazards.Heat + 2) } }, - EditorTool.Fire => cell with - { - Hazards = cell.Hazards with - { + EEditorTool.Heat => cell with { Hazards = cell.Hazards with { Heat = Rules.Clamp(cell.Hazards.Heat + 2) } }, + EEditorTool.Fire => cell with { + Hazards = cell.Hazards with { Fire = !cell.Hazards.Fire, Heat = Math.Max(cell.Hazards.Heat, 7), Smoke = Math.Max(cell.Hazards.Smoke, 3) @@ -114,7 +100,7 @@ public static class LevelEditor _ => cell }; - if (cell.Kind == CellKind.Wall) + if (cell.Kind == ECellKind.Wall) cell = cell with { Hazards = new() }; return level.SetCell(position, cell); diff --git a/src/ReactorMaintenance.Simulation/LevelSerializer.cs b/src/ReactorMaintenance.Simulation/LevelSerializer.cs index 8d3fc64..d03aed4 100644 --- a/src/ReactorMaintenance.Simulation/LevelSerializer.cs +++ b/src/ReactorMaintenance.Simulation/LevelSerializer.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace ReactorMaintenance.Simulation; @@ -13,15 +13,10 @@ public static class LevelSerializer public static LevelState Deserialize(string json) { var level = JsonSerializer.Deserialize(json, Options) ?? throw new InvalidOperationException("Level file did not contain a level."); - - if (level.Cells.Length != level.Width * level.Height) - throw new InvalidOperationException("Level cell count does not match its dimensions."); - - return level; + return level.Cells.Length != level.Width * level.Height ? throw new InvalidOperationException("Level cell count does not match its dimensions.") : level; } - private static readonly JsonSerializerOptions Options = new() - { + private static readonly JsonSerializerOptions Options = new() { WriteIndented = true, Converters = { new JsonStringEnumConverter() } }; diff --git a/src/ReactorMaintenance.Simulation/Models.cs b/src/ReactorMaintenance.Simulation/Models.cs index 64ad37e..019d3bd 100644 --- a/src/ReactorMaintenance.Simulation/Models.cs +++ b/src/ReactorMaintenance.Simulation/Models.cs @@ -1,6 +1,6 @@ -namespace ReactorMaintenance.Simulation; +namespace ReactorMaintenance.Simulation; -public enum CellKind +public enum ECellKind { Empty, Floor, @@ -13,7 +13,7 @@ public enum CellKind ControlTerminal } -public enum PipeMedium +public enum EPipeMedium { None, Pressure, @@ -21,7 +21,7 @@ public enum PipeMedium Fuel } -public enum FailureKind +public enum EFailureKind { PipeBurst, Ignition, @@ -45,8 +45,7 @@ public sealed record HazardState { public HazardState Clamp() { - return this with - { + return this with { Heat = Rules.Clamp(Heat), Smoke = Rules.Clamp(Smoke), FuelVapor = Rules.Clamp(FuelVapor), @@ -69,8 +68,8 @@ public sealed record HazardState public sealed record CellState { - public CellKind Kind { get; init; } = CellKind.Floor; - public PipeMedium Pipe { get; init; } + public ECellKind Kind { get; init; } = ECellKind.Floor; + public EPipeMedium Pipe { get; init; } public int Flow { get; init; } public int Pressure { get; init; } public int Integrity { get; init; } = 10; @@ -79,9 +78,8 @@ public sealed record CellState public bool Powered { get; init; } public bool DoorLocked { get; init; } public HazardState Hazards { get; init; } = new(); - - public bool IsWalkable => Kind != CellKind.Wall && Kind != CellKind.Empty; - public bool HasPipe => Pipe != PipeMedium.None; + public bool IsWalkable => Kind != ECellKind.Wall && Kind != ECellKind.Empty; + public bool HasPipe => Pipe != EPipeMedium.None; } public sealed record GlobalState @@ -97,7 +95,7 @@ public sealed record GlobalState public string Status { get; init; } = "STABILIZE SYSTEMS"; } -public sealed record Forecast(FailureKind Kind, GridPosition? Position, int Turns, string Message); +public sealed record Forecast(EFailureKind Kind, GridPosition? Position, int Turns, string Message); public sealed record LevelState { @@ -108,12 +106,15 @@ public sealed record LevelState var cells = CreateCells(width, height); for (var y = 0; y < height; y++) - for (var x = 0; x < width; x++) - if (x == 0 || y == 0 || x == width - 1 || y == height - 1) - cells[y * width + x] = cells[y * width + x] with { Kind = CellKind.Wall }; - - return new() { + for (var x = 0; x < width; x++) + { + if (x == 0 || y == 0 || x == width - 1 || y == height - 1) + cells[y * width + x] = cells[y * width + x] with { Kind = ECellKind.Wall }; + } + } + + return new() { Name = name, Width = width, Height = height, diff --git a/src/ReactorMaintenance.Simulation/SimulationEngine.cs b/src/ReactorMaintenance.Simulation/SimulationEngine.cs index 814fc42..a69acbe 100644 --- a/src/ReactorMaintenance.Simulation/SimulationEngine.cs +++ b/src/ReactorMaintenance.Simulation/SimulationEngine.cs @@ -1,4 +1,4 @@ -namespace ReactorMaintenance.Simulation; +namespace ReactorMaintenance.Simulation; public sealed class SimulationEngine { @@ -7,52 +7,51 @@ public sealed class SimulationEngine var cells = level.Cells.ToArray(); for (var y = 0; y < level.Height; y++) - for (var x = 0; x < level.Width; x++) { - var position = new GridPosition(x, y); - var index = level.Index(position); - var cell = cells[index]; - - if (!cell.IsWalkable) - continue; - - var hazards = ApplyPipeLeaks(cell); - hazards = ApplyMachineEffects(cell, hazards); - hazards = ApplyFireAndElectricalHazards(cell, hazards); - hazards = hazards.Clamp(); - - var integrity = cell.Integrity; - if (cell.HasPipe && cell.Pressure > 7) - integrity -= cell.Pressure - 7; - - if (hazards.Heat >= 10 || hazards.Fire) + for (var x = 0; x < level.Width; x++) { - integrity -= cell.HasPipe ? 1 : 0; - hazards = hazards with { Stability = hazards.Stability - 1 }; - } + var position = new GridPosition(x, y); + var index = level.Index(position); + var cell = cells[index]; - if (integrity <= 0 && cell.HasPipe) - { - cell = cell with + if (!cell.IsWalkable) + continue; + + var hazards = ApplyPipeLeaks(cell); + hazards = ApplyMachineEffects(cell, hazards); + hazards = ApplyFireAndElectricalHazards(cell, hazards); + hazards = hazards.Clamp(); + + var integrity = cell.Integrity; + if (cell is { HasPipe: true, Pressure: > 7 }) + integrity -= cell.Pressure - 7; + + if (hazards.Heat >= 10 || hazards.Fire) { - LeakRate = Math.Max(cell.LeakRate, 3), - Flow = 0, - PipeOpen = false + integrity -= cell.HasPipe ? 1 : 0; + hazards = hazards with { Stability = hazards.Stability - 1 }; + } + + if (integrity <= 0 && cell.HasPipe) + { + cell = cell with { + LeakRate = Math.Max(cell.LeakRate, 3), + Flow = 0, + PipeOpen = false + }; + } + + cells[index] = cell with { + Integrity = Rules.Clamp(integrity), + Hazards = hazards.Clamp() }; } - - cells[index] = cell with - { - Integrity = Rules.Clamp(integrity), - Hazards = hazards.Clamp() - }; } cells = SpreadSmoke(level, cells); var global = UpdateGlobal(level, cells); - var next = level with - { + var next = level with { Cells = cells, Global = global with { Turn = level.Global.Turn + 1 } }; @@ -70,25 +69,25 @@ public sealed class SimulationEngine var position = new GridPosition(x, y); var cell = level.GetCell(position); - if (cell.HasPipe && cell.Pressure > 7 && cell.Integrity > 0) + if (cell is { HasPipe: true, Pressure: > 7, Integrity: > 0 }) { var damagePerTurn = Math.Max(1, cell.Pressure - 7); var turns = (int)Math.Ceiling(cell.Integrity / (double)damagePerTurn); if (turns <= 4) - forecasts.Add(new(FailureKind.PipeBurst, position, turns, $"PIPE BURST PREDICTED AT {x},{y} IN {turns} TURNS")); + forecasts.Add(new(EFailureKind.PipeBurst, position, turns, $"PIPE BURST PREDICTED AT {x},{y} IN {turns} TURNS")); } - var fuelLeakNearIgnition = cell.Pipe == PipeMedium.Fuel && cell.LeakRate > 0 && (cell.Pressure >= 7 || cell.Kind == CellKind.Generator); - var ignitionRisk = (cell.Hazards.FuelVapor >= 4 || cell.Hazards.LiquidFuel >= 6 || fuelLeakNearIgnition) && (cell.Hazards.Heat >= 8 || cell.Hazards.ElectricalCharge >= 4 || cell.Kind == CellKind.Generator); + var fuelLeakNearIgnition = cell is { Pipe: EPipeMedium.Fuel, LeakRate: > 0 } && (cell.Pressure >= 7 || cell.Kind == ECellKind.Generator); + var ignitionRisk = (cell.Hazards.FuelVapor >= 4 || cell.Hazards.LiquidFuel >= 6 || fuelLeakNearIgnition) && (cell.Hazards.Heat >= 8 || cell.Hazards.ElectricalCharge >= 4 || cell.Kind == ECellKind.Generator); if (ignitionRisk && !cell.Hazards.Fire) - forecasts.Add(new(FailureKind.Ignition, position, 1, $"FUEL IGNITION PREDICTED AT {x},{y} NEXT TURN")); + forecasts.Add(new(EFailureKind.Ignition, position, 1, $"FUEL IGNITION PREDICTED AT {x},{y} NEXT TURN")); } if (level.Global.CoreHeat >= 8) - forecasts.Add(new(FailureKind.Meltdown, null, Math.Max(1, 11 - level.Global.CoreHeat), "CORE MELTDOWN APPROACHING")); + forecasts.Add(new(EFailureKind.Meltdown, null, Math.Max(1, 11 - level.Global.CoreHeat), "CORE MELTDOWN APPROACHING")); if (IsReactorReady(level)) - forecasts.Add(new(FailureKind.ReactorReady, null, 0, "REACTOR READY")); + forecasts.Add(new(EFailureKind.ReactorReady, null, 0, "REACTOR READY")); return forecasts.OrderBy(f => f.Turns).ThenBy(f => f.Message).ToArray(); } @@ -98,10 +97,8 @@ public sealed class SimulationEngine if (!IsReactorReady(level)) return level with { Global = level.Global with { Status = "REACTOR NOT READY" } }; - return level with - { - Global = level.Global with - { + return level with { + Global = level.Global with { ReactorActivated = true, Status = "REACTOR ONLINE" } @@ -114,32 +111,28 @@ public sealed class SimulationEngine if (!cell.HasPipe || cell.LeakRate <= 0) return hazards; - return cell.Pipe switch - { - PipeMedium.Fuel => hazards with - { + return cell.Pipe switch { + EPipeMedium.Fuel => hazards with { LiquidFuel = hazards.LiquidFuel + cell.LeakRate, FuelVapor = hazards.FuelVapor + (cell.Pressure >= 7 ? cell.LeakRate : Math.Max(0, hazards.Heat - 3) / 3) }, - PipeMedium.Coolant => hazards with - { + EPipeMedium.Coolant => hazards with { CoolantPooling = hazards.CoolantPooling + cell.LeakRate, Heat = hazards.Heat - Math.Max(1, cell.LeakRate / 2), Smoke = hazards.Smoke + (hazards.Heat >= 7 ? 2 : 0) }, - PipeMedium.Pressure => hazards with { Smoke = hazards.Smoke + (cell.Pressure >= 8 ? 1 : 0) }, - _ => hazards + EPipeMedium.Pressure => hazards with { Smoke = hazards.Smoke + (cell.Pressure >= 8 ? 1 : 0) }, + _ => hazards }; } private static HazardState ApplyMachineEffects(CellState cell, HazardState hazards) { - return cell.Kind switch - { - CellKind.Generator when cell.Powered => hazards with { Heat = hazards.Heat + 1 }, - CellKind.CoolingPump when cell.Powered => hazards with { Heat = hazards.Heat - 2 }, - CellKind.Reactor => hazards with { Heat = hazards.Heat + 1 }, - _ => hazards + return cell.Kind switch { + ECellKind.Generator when cell.Powered => hazards with { Heat = hazards.Heat + 1 }, + ECellKind.CoolingPump when cell.Powered => hazards with { Heat = hazards.Heat - 2 }, + ECellKind.Reactor => hazards with { Heat = hazards.Heat + 1 }, + _ => hazards }; } @@ -149,11 +142,10 @@ public sealed class SimulationEngine hazards = hazards with { ElectricalCharge = hazards.ElectricalCharge + 2 }; var hasFuel = hazards.FuelVapor >= 4 || hazards.LiquidFuel >= 6; - var hasIgnition = hazards.Heat >= 8 || hazards.ElectricalCharge >= 4 || (cell.Kind == CellKind.Generator && cell.Powered); + var hasIgnition = hazards.Heat >= 8 || hazards.ElectricalCharge >= 4 || cell is { Kind: ECellKind.Generator, Powered: true }; if ((hasFuel && hasIgnition) || hazards.Fire) { - hazards = hazards with - { + hazards = hazards with { Fire = hasFuel || hazards.Fire, Heat = hazards.Heat + 2, Smoke = hazards.Smoke + 2, @@ -171,20 +163,22 @@ public sealed class SimulationEngine { var next = cells.ToArray(); for (var y = 0; y < level.Height; y++) - for (var x = 0; x < level.Width; x++) { - var position = new GridPosition(x, y); - var cell = cells[level.Index(position)]; - if (cell.Hazards.Smoke < 6) - continue; - - foreach (var neighbor in position.Neighbors().Where(level.InBounds)) + for (var x = 0; x < level.Width; x++) { - var neighborCell = next[level.Index(neighbor)]; - if (!neighborCell.IsWalkable || neighborCell.DoorLocked) + var position = new GridPosition(x, y); + var cell = cells[level.Index(position)]; + if (cell.Hazards.Smoke < 6) continue; - next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + 1) } }; + foreach (var neighbor in position.Neighbors().Where(level.InBounds)) + { + var neighborCell = next[level.Index(neighbor)]; + if (!neighborCell.IsWalkable || neighborCell.DoorLocked) + continue; + + next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + 1) } }; + } } } @@ -193,18 +187,14 @@ public sealed class SimulationEngine private static GlobalState UpdateGlobal(LevelState level, CellState[] cells) { - var reactorHeat = cells.Where(c => c.Kind == CellKind.Reactor).Select(c => c.Hazards.Heat).DefaultIfEmpty(level.Global.CoreHeat).Max(); - - var poweredGenerators = cells.Count(c => c.Kind == CellKind.Generator && c.Powered && !c.Hazards.Fire); - var poweredPumps = cells.Count(c => c.Kind == CellKind.CoolingPump && c.Powered && !c.Hazards.Fire); - var damagedCriticalCells = cells.Count(c => c.Kind is CellKind.Reactor or CellKind.Generator or CellKind.CoolingPump && c.Hazards.Stability <= 3); + var reactorHeat = cells.Where(c => c.Kind == ECellKind.Reactor).Select(c => c.Hazards.Heat).DefaultIfEmpty(level.Global.CoreHeat).Max(); + var poweredGenerators = cells.Count(c => c is { Kind: ECellKind.Generator, Powered: true, Hazards.Fire: false }); + var poweredPumps = cells.Count(c => c is { Kind: ECellKind.CoolingPump, Powered: true, Hazards.Fire: false }); + var damagedCriticalCells = cells.Count(c => c.Kind is ECellKind.Reactor or ECellKind.Generator or ECellKind.CoolingPump && c.Hazards.Stability <= 3); var stability = Rules.Clamp(level.Global.FacilityStability - damagedCriticalCells); var lost = reactorHeat >= 10 || stability <= 0; - var status = lost ? reactorHeat >= 10 ? "CORE MELTDOWN" : "FACILITY STABILITY COLLAPSE" : "STABILIZE SYSTEMS"; - - var global = level.Global with - { + var global = level.Global with { CoreHeat = Rules.Clamp(reactorHeat - poweredPumps), Power = Rules.Clamp(poweredGenerators * 3), Cooling = Rules.Clamp(poweredPumps * 3), @@ -213,20 +203,14 @@ public sealed class SimulationEngine Status = status }; - return IsReactorReady(level with - { - Cells = cells, - Global = global - }) - ? global with { Status = "REACTOR READY" } - : global; + return IsReactorReady(level with { Cells = cells, Global = global }) ? global with { Status = "REACTOR READY" } : global; } private static bool IsReactorReady(LevelState level) { - var hasReactor = level.Cells.Any(c => c.Kind == CellKind.Reactor); - var hasStablePower = level.Global.Power >= 3 || level.Cells.Any(c => c.Kind == CellKind.Generator && c.Powered && !c.Hazards.Fire); - var hasCooling = level.Global.Cooling >= 3 || level.Cells.Any(c => c.Kind == CellKind.CoolingPump && c.Powered && !c.Hazards.Fire); + var hasReactor = level.Cells.Any(c => c.Kind == ECellKind.Reactor); + var hasStablePower = level.Global.Power >= 3 || level.Cells.Any(c => c is { Kind: ECellKind.Generator, Powered: true, Hazards.Fire: false }); + var hasCooling = level.Global.Cooling >= 3 || level.Cells.Any(c => c is { Kind: ECellKind.CoolingPump, Powered: true, Hazards.Fire: false }); var reactorStable = level.Global.CoreHeat < 8; return hasReactor && hasStablePower && hasCooling && reactorStable && !level.Global.Lost; } diff --git a/src/ReactorMaintenance.Win2D/App.xaml b/src/ReactorMaintenance.Win2D/App.xaml index 3fc383f..2ee7cdd 100644 --- a/src/ReactorMaintenance.Win2D/App.xaml +++ b/src/ReactorMaintenance.Win2D/App.xaml @@ -1,4 +1,4 @@ - diff --git a/src/ReactorMaintenance.Win2D/App.xaml.cs b/src/ReactorMaintenance.Win2D/App.xaml.cs index 3161abc..3bb964e 100644 --- a/src/ReactorMaintenance.Win2D/App.xaml.cs +++ b/src/ReactorMaintenance.Win2D/App.xaml.cs @@ -1,8 +1,8 @@ -using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml; namespace ReactorMaintenance.Win2D; -public partial class App : Application +public partial class App { public App() { @@ -11,9 +11,9 @@ public partial class App : Application protected override void OnLaunched(LaunchActivatedEventArgs args) { - _window = new MainWindow(); - _window.Activate(); + m_Window = new MainWindow(); + m_Window.Activate(); } - private Window? _window; + private Window? m_Window; } \ No newline at end of file diff --git a/src/ReactorMaintenance.Win2D/MainWindow.xaml b/src/ReactorMaintenance.Win2D/MainWindow.xaml index b9e56e1..44e307f 100644 --- a/src/ReactorMaintenance.Win2D/MainWindow.xaml +++ b/src/ReactorMaintenance.Win2D/MainWindow.xaml @@ -1,4 +1,4 @@ -(); - ToolPicker.SelectedItem = _selectedTool; + m_Level = BuildStarterLevel(); + ToolPicker.ItemsSource = Enum.GetValues(); + ToolPicker.SelectedItem = m_SelectedTool; RefreshInspector(); } private void ToolPicker_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (ToolPicker.SelectedItem is EditorTool tool) - _selectedTool = tool; + if (ToolPicker.SelectedItem is EEditorTool tool) + m_SelectedTool = tool; } private void New_Click(object sender, RoutedEventArgs e) { - _level = BuildStarterLevel(); - _currentFile = null; - _selectedCell = null; + m_Level = BuildStarterLevel(); + m_CurrentFile = null; + m_SelectedCell = null; RefreshInspector(); LevelCanvas.Invalidate(); } - private async void Open_Click(object sender, RoutedEventArgs e) + private async void Open_Click(object sender, RoutedEventArgs args) { - var picker = new FileOpenPicker(); - InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(this)); - picker.FileTypeFilter.Add(".json"); - - var file = await picker.PickSingleFileAsync(); - if (file is null) - return; - - var json = await FileIO.ReadTextAsync(file); - _level = LevelSerializer.Deserialize(json); - _level = _level with { Forecasts = _simulation.Forecast(_level) }; - _currentFile = file; - _selectedCell = null; - RefreshInspector(); - LevelCanvas.Invalidate(); - } - - private async void Save_Click(object sender, RoutedEventArgs e) - { - var file = _currentFile; - if (file is null) + try { - var picker = new FileSavePicker(); + var picker = new FileOpenPicker(); InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(this)); - picker.SuggestedFileName = _level.Name.Replace(' ', '-').ToLowerInvariant(); - picker.FileTypeChoices.Add("Reactor level", new List { ".json" }); - file = await picker.PickSaveFileAsync(); + picker.FileTypeFilter.Add(".json"); + + var file = await picker.PickSingleFileAsync(); + if (file is null) + return; + + var json = await FileIO.ReadTextAsync(file); + m_Level = LevelSerializer.Deserialize(json); + m_Level = m_Level with { Forecasts = m_Simulation.Forecast(m_Level) }; + m_CurrentFile = file; + m_SelectedCell = null; + RefreshInspector(); + LevelCanvas.Invalidate(); } + catch (Exception e) + { + var messageDialog = new MessageDialog(e.Message); + _ = await messageDialog.ShowAsync(); + } + } - if (file is null) - return; + private async void Save_Click(object sender, RoutedEventArgs args) + { + try + { + var file = m_CurrentFile; + if (file is null) + { + var picker = new FileSavePicker(); + InitializeWithWindow.Initialize(picker, WindowNative.GetWindowHandle(this)); + picker.SuggestedFileName = m_Level.Name.Replace(' ', '-').ToLowerInvariant(); + picker.FileTypeChoices.Add("Reactor level", [".json"]); + file = await picker.PickSaveFileAsync(); + } - await FileIO.WriteTextAsync(file, LevelSerializer.Serialize(_level)); - _currentFile = file; + if (file is null) + return; + + await FileIO.WriteTextAsync(file, LevelSerializer.Serialize(m_Level)); + m_CurrentFile = file; + } + catch (Exception e) + { + var messageDialog = new MessageDialog(e.Message); + _ = await messageDialog.ShowAsync(); + } } private void Simulate_Click(object sender, RoutedEventArgs e) { - _level = _simulation.AdvanceTurn(_level); + m_Level = m_Simulation.AdvanceTurn(m_Level); RefreshInspector(); LevelCanvas.Invalidate(); } private void Activate_Click(object sender, RoutedEventArgs e) { - _level = _simulation.ActivateReactor(_level); + m_Level = m_Simulation.ActivateReactor(m_Level); RefreshInspector(); LevelCanvas.Invalidate(); } private void LevelCanvas_PointerPressed(object sender, PointerRoutedEventArgs e) { - _painting = true; - LevelCanvas.CapturePointer(e.Pointer); + m_Painting = true; + _ = LevelCanvas.CapturePointer(e.Pointer); PaintAt(e.GetCurrentPoint(LevelCanvas).Position); } private void LevelCanvas_PointerMoved(object sender, PointerRoutedEventArgs e) { - if (_painting) + if (m_Painting) PaintAt(e.GetCurrentPoint(LevelCanvas).Position); } private void LevelCanvas_PointerReleased(object sender, PointerRoutedEventArgs e) { - _painting = false; + m_Painting = false; LevelCanvas.ReleasePointerCapture(e.Pointer); } @@ -126,9 +144,9 @@ public sealed partial class MainWindow : Window if (!TryGetGridPosition(point, out var position)) return; - _selectedCell = position; - _level = LevelEditor.Apply(_level, position, _selectedTool); - _level = _level with { Forecasts = _simulation.Forecast(_level) }; + m_SelectedCell = position; + m_Level = LevelEditor.Apply(m_Level, position, m_SelectedTool); + m_Level = m_Level with { Forecasts = m_Simulation.Forecast(m_Level) }; RefreshInspector(); LevelCanvas.Invalidate(); } @@ -146,11 +164,11 @@ public sealed partial class MainWindow : Window private void DrawCells(CanvasDrawingSession drawing, CanvasLayout layout) { - for (var y = 0; y < _level.Height; y++) - for (var x = 0; x < _level.Width; x++) + for (var y = 0; y < m_Level.Height; y++) + for (var x = 0; x < m_Level.Width; x++) { var position = new GridPosition(x, y); - var cell = _level.GetCell(position); + var cell = m_Level.GetCell(position); var rect = layout.CellRect(x, y); drawing.FillRectangle(rect, CellColor(cell)); @@ -158,15 +176,14 @@ public sealed partial class MainWindow : Window if (cell.HasPipe) { var center = new Vector2((float)(rect.X + rect.Width / 2), (float)(rect.Y + rect.Height / 2)); - var pipeColor = cell.Pipe switch - { - PipeMedium.Coolant => Colors.DeepSkyBlue, - PipeMedium.Fuel => Colors.Goldenrod, - PipeMedium.Pressure => Colors.LightSteelBlue, - _ => Colors.Transparent + var pipeColor = cell.Pipe switch { + EPipeMedium.Coolant => Colors.DeepSkyBlue, + EPipeMedium.Fuel => Colors.Goldenrod, + EPipeMedium.Pressure => Colors.LightSteelBlue, + _ => Colors.Transparent }; - drawing.DrawLine(new((float)rect.X + 6, center.Y), new((float)(rect.X + rect.Width - 6), center.Y), pipeColor, Math.Max(3, (float)rect.Width / 7)); - drawing.DrawLine(new(center.X, (float)rect.Y + 6), new(center.X, (float)(rect.Y + rect.Height - 6)), pipeColor, Math.Max(3, (float)rect.Width / 7)); + drawing.DrawLine(center with { X = (float)rect.X + 6 }, center with { X = (float)(rect.X + rect.Width - 6) }, pipeColor, Math.Max(3, (float)rect.Width / 7)); + drawing.DrawLine(center with { Y = (float)rect.Y + 6 }, center with { Y = (float)(rect.Y + rect.Height - 6) }, pipeColor, Math.Max(3, (float)rect.Width / 7)); } if (cell.LeakRate > 0) @@ -175,7 +192,7 @@ public sealed partial class MainWindow : Window if (cell.Hazards.Fire) drawing.FillCircle(new((float)(rect.X + rect.Width * 0.5), (float)(rect.Y + rect.Height * 0.5)), (float)rect.Width * 0.24f, Colors.OrangeRed); - if (_selectedCell == position) + if (m_SelectedCell == position) drawing.DrawRectangle(rect, Colors.White, 3); DrawCellGlyph(drawing, cell, rect); @@ -184,48 +201,45 @@ public sealed partial class MainWindow : Window private void DrawCellGlyph(CanvasDrawingSession drawing, CellState cell, Rect rect) { - var text = cell.Kind switch - { - CellKind.Reactor => "R", - CellKind.CoolingPump => "C", - CellKind.Generator => "G", - CellKind.PressureRegulator => "P", - CellKind.DiagnosticTerminal => "D", - CellKind.ControlTerminal => "T", - _ => string.Empty + var text = cell.Kind switch { + ECellKind.Reactor => "R", + ECellKind.CoolingPump => "C", + ECellKind.Generator => "G", + ECellKind.PressureRegulator => "P", + ECellKind.DiagnosticTerminal => "D", + ECellKind.ControlTerminal => "T", + _ => string.Empty }; if (string.IsNullOrEmpty(text)) return; - using var format = new CanvasTextFormat - { - FontSize = Math.Max(14, (float)rect.Width * 0.42f), - HorizontalAlignment = CanvasHorizontalAlignment.Center, - VerticalAlignment = CanvasVerticalAlignment.Center - }; + using var format = new CanvasTextFormat(); + format.FontSize = Math.Max(14, (float)rect.Width * 0.42f); + format.HorizontalAlignment = CanvasHorizontalAlignment.Center; + format.VerticalAlignment = CanvasVerticalAlignment.Center; drawing.DrawText(text, rect, Colors.White, format); } private void DrawGrid(CanvasDrawingSession drawing, CanvasLayout layout) { - for (var x = 0; x <= _level.Width; x++) + for (var x = 0; x <= m_Level.Width; x++) { var xPos = (float)(layout.OriginX + x * layout.CellSize); - drawing.DrawLine(xPos, (float)layout.OriginY, xPos, (float)(layout.OriginY + _level.Height * layout.CellSize), ColorHelper.FromArgb(120, 91, 104, 115), 1); + drawing.DrawLine(xPos, (float)layout.OriginY, xPos, (float)(layout.OriginY + m_Level.Height * layout.CellSize), ColorHelper.FromArgb(120, 91, 104, 115), 1); } - for (var y = 0; y <= _level.Height; y++) + for (var y = 0; y <= m_Level.Height; y++) { var yPos = (float)(layout.OriginY + y * layout.CellSize); - drawing.DrawLine((float)layout.OriginX, yPos, (float)(layout.OriginX + _level.Width * layout.CellSize), yPos, ColorHelper.FromArgb(120, 91, 104, 115), 1); + drawing.DrawLine((float)layout.OriginX, yPos, (float)(layout.OriginX + m_Level.Width * layout.CellSize), yPos, ColorHelper.FromArgb(120, 91, 104, 115), 1); } } private void DrawRobot(CanvasDrawingSession drawing, CanvasLayout layout) { - var rect = layout.CellRect(_level.Robot.X, _level.Robot.Y); + var rect = layout.CellRect(m_Level.Robot.X, m_Level.Robot.Y); var center = new Vector2((float)(rect.X + rect.Width / 2), (float)(rect.Y + rect.Height / 2)); drawing.FillCircle(center, (float)rect.Width * 0.28f, Colors.White); drawing.DrawCircle(center, (float)rect.Width * 0.28f, Colors.Black, 2); @@ -237,123 +251,113 @@ public sealed partial class MainWindow : Window var x = (int)((point.X - layout.OriginX) / layout.CellSize); var y = (int)((point.Y - layout.OriginY) / layout.CellSize); position = new(x, y); - return _level.InBounds(position); + return m_Level.InBounds(position); } private CanvasLayout GetLayout() { var availableWidth = Math.Max(1, LevelCanvas.ActualWidth); var availableHeight = Math.Max(1, LevelCanvas.ActualHeight); - var size = Math.Floor(Math.Min(availableWidth / _level.Width, availableHeight / _level.Height)); + var size = Math.Floor(Math.Min(availableWidth / m_Level.Width, availableHeight / m_Level.Height)); size = Math.Max(20, size); - var originX = Math.Max(0, (availableWidth - size * _level.Width) / 2); - var originY = Math.Max(0, (availableHeight - size * _level.Height) / 2); + var originX = Math.Max(0, (availableWidth - size * m_Level.Width) / 2); + var originY = Math.Max(0, (availableHeight - size * m_Level.Height) / 2); return new(size, originX, originY); } private static Color CellColor(CellState cell) { - if (cell.Kind == CellKind.Wall) + if (cell.Kind == ECellKind.Wall) return ColorHelper.FromArgb(255, 54, 61, 68); if (cell.Hazards.Fire) return ColorHelper.FromArgb(255, 91, 39, 30); - return cell.Kind switch - { - CellKind.Reactor => ColorHelper.FromArgb(255, 61, 76, 82), - CellKind.CoolingPump => ColorHelper.FromArgb(255, 25, 79, 96), - CellKind.Generator => ColorHelper.FromArgb(255, 86, 75, 35), - CellKind.PressureRegulator => ColorHelper.FromArgb(255, 70, 78, 98), - CellKind.DiagnosticTerminal => ColorHelper.FromArgb(255, 39, 84, 62), - CellKind.ControlTerminal => ColorHelper.FromArgb(255, 80, 61, 91), - _ => ColorHelper.FromArgb(255, 31, 36, 40) + return cell.Kind switch { + ECellKind.Reactor => ColorHelper.FromArgb(255, 61, 76, 82), + ECellKind.CoolingPump => ColorHelper.FromArgb(255, 25, 79, 96), + ECellKind.Generator => ColorHelper.FromArgb(255, 86, 75, 35), + ECellKind.PressureRegulator => ColorHelper.FromArgb(255, 70, 78, 98), + ECellKind.DiagnosticTerminal => ColorHelper.FromArgb(255, 39, 84, 62), + ECellKind.ControlTerminal => ColorHelper.FromArgb(255, 80, 61, 91), + _ => ColorHelper.FromArgb(255, 31, 36, 40) }; } private void RefreshInspector() { - LevelNameText.Text = _level.Name; - TurnText.Text = _level.Global.Turn.ToString(); - StatusText.Text = _level.Global.Status; - GlobalText.Text = $"Power: {_level.Global.Power}/10\n" + $"Cooling: {_level.Global.Cooling}/10\n" + $"Core Heat: {_level.Global.CoreHeat}/10\n" + $"Facility Stability: {_level.Global.FacilityStability}/10"; + LevelNameText.Text = m_Level.Name; + TurnText.Text = m_Level.Global.Turn.ToString(CultureInfo.InvariantCulture); + StatusText.Text = m_Level.Global.Status; + GlobalText.Text = $"Power: {m_Level.Global.Power}/10\n" + $"Cooling: {m_Level.Global.Cooling}/10\n" + $"Core Heat: {m_Level.Global.CoreHeat}/10\n" + $"Facility Stability: {m_Level.Global.FacilityStability}/10"; - if (_selectedCell is { } position && _level.InBounds(position)) + if (m_SelectedCell is { } position && m_Level.InBounds(position)) { - var cell = _level.GetCell(position); + var cell = m_Level.GetCell(position); CellText.Text = $"Position: {position.X},{position.Y}\n" + $"Kind: {cell.Kind}\n" + $"Pipe: {cell.Pipe}\n" + $"Flow: {cell.Flow}, Pressure: {cell.Pressure}\n" + $"Integrity: {cell.Integrity}, Leak: {cell.LeakRate}\n" + $"Heat: {cell.Hazards.Heat}, Smoke: {cell.Hazards.Smoke}\n" + $"Fuel Vapor: {cell.Hazards.FuelVapor}, Fuel: {cell.Hazards.LiquidFuel}\n" + $"Coolant: {cell.Hazards.CoolantPooling}, Charge: {cell.Hazards.ElectricalCharge}"; } else CellText.Text = "No cell selected."; - ForecastList.ItemsSource = _level.Forecasts; + ForecastList.ItemsSource = m_Level.Forecasts; } private static LevelState BuildStarterLevel() { var level = LevelState.Create("Cooling Sector B", 16, 12); - level = level.SetCell(new(3, 5), new() - { - Kind = CellKind.CoolingPump, - Pipe = PipeMedium.Coolant, + level = level.SetCell(new(3, 5), new() { + Kind = ECellKind.CoolingPump, + Pipe = EPipeMedium.Coolant, Flow = 5, Pressure = 5, Powered = true }); - level = level.SetCell(new(4, 5), new() - { - Pipe = PipeMedium.Coolant, + level = level.SetCell(new(4, 5), new() { + Pipe = EPipeMedium.Coolant, Flow = 5, Pressure = 7 }); - level = level.SetCell(new(5, 5), new() - { - Pipe = PipeMedium.Coolant, + level = level.SetCell(new(5, 5), new() { + Pipe = EPipeMedium.Coolant, Flow = 3, Pressure = 8, LeakRate = 2, Integrity = 4 }); - level = level.SetCell(new(6, 5), new() - { - Pipe = PipeMedium.Coolant, + level = level.SetCell(new(6, 5), new() { + Pipe = EPipeMedium.Coolant, Flow = 3, Pressure = 7 }); - level = level.SetCell(new(8, 5), new() - { - Kind = CellKind.Reactor, - Hazards = new() - { + level = level.SetCell(new(8, 5), new() { + Kind = ECellKind.Reactor, + Hazards = new() { Heat = 6, Stability = 8 } }); - level = level.SetCell(new(2, 8), new() - { - Kind = CellKind.Generator, - Pipe = PipeMedium.Fuel, + level = level.SetCell(new(2, 8), new() { + Kind = ECellKind.Generator, + Pipe = EPipeMedium.Fuel, Flow = 4, Pressure = 6, Powered = true }); - level = level.SetCell(new(11, 4), new() - { - Kind = CellKind.DiagnosticTerminal, + level = level.SetCell(new(11, 4), new() { + Kind = ECellKind.DiagnosticTerminal, Powered = true }); - level = level.SetCell(new(12, 8), new() - { - Kind = CellKind.ControlTerminal, + level = level.SetCell(new(12, 8), new() { + Kind = ECellKind.ControlTerminal, Powered = true }); return level with { Forecasts = new SimulationEngine().Forecast(level) }; } - private readonly SimulationEngine _simulation = new(); - private StorageFile? _currentFile; - private LevelState _level; - private bool _painting; - private GridPosition? _selectedCell; - private EditorTool _selectedTool = EditorTool.Floor; + private readonly SimulationEngine m_Simulation = new(); + private StorageFile? m_CurrentFile; + private LevelState m_Level; + private bool m_Painting; + private GridPosition? m_SelectedCell; + private EEditorTool m_SelectedTool = EEditorTool.Floor; } \ No newline at end of file diff --git a/src/ReactorMaintenance.Win2D/ReactorMaintenance.Win2D.csproj b/src/ReactorMaintenance.Win2D/ReactorMaintenance.Win2D.csproj index 2355f15..7d3910e 100644 --- a/src/ReactorMaintenance.Win2D/ReactorMaintenance.Win2D.csproj +++ b/src/ReactorMaintenance.Win2D/ReactorMaintenance.Win2D.csproj @@ -1,26 +1,27 @@ - - - WinExe - net8.0-windows10.0.19041.0 - 10.0.17763.0 - ReactorMaintenance.Win2D - app.manifest - x86;x64;arm64 - win-x86;win-x64;win-arm64 - true - enable - enable - None - true - + + + WinExe + net8.0-windows10.0.19041.0 + 10.0.17763.0 + ReactorMaintenance.Win2D + app.manifest + x86;x64;arm64 + win-x86;win-x64;win-arm64 + true + enable + enable + None + true + true + - - - - - + + + + + - - - - + + + + \ No newline at end of file diff --git a/src/ReactorMaintenance.Win2D/app.manifest b/src/ReactorMaintenance.Win2D/app.manifest index b15d8d1..6ad7df3 100644 --- a/src/ReactorMaintenance.Win2D/app.manifest +++ b/src/ReactorMaintenance.Win2D/app.manifest @@ -1,4 +1,4 @@ - + diff --git a/tests/ReactorMaintenance.Simulation.Tests/ReactorMaintenance.Simulation.Tests.csproj b/tests/ReactorMaintenance.Simulation.Tests/ReactorMaintenance.Simulation.Tests.csproj index 8e793b4..5edf316 100644 --- a/tests/ReactorMaintenance.Simulation.Tests/ReactorMaintenance.Simulation.Tests.csproj +++ b/tests/ReactorMaintenance.Simulation.Tests/ReactorMaintenance.Simulation.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 diff --git a/tests/ReactorMaintenance.Simulation.Tests/SimulationEngineTests.cs b/tests/ReactorMaintenance.Simulation.Tests/SimulationEngineTests.cs index a46cf0d..d15dd3d 100644 --- a/tests/ReactorMaintenance.Simulation.Tests/SimulationEngineTests.cs +++ b/tests/ReactorMaintenance.Simulation.Tests/SimulationEngineTests.cs @@ -1,36 +1,36 @@ -namespace ReactorMaintenance.Simulation.Tests; +namespace ReactorMaintenance.Simulation.Tests; public sealed class SimulationEngineTests { [Fact] public void FuelLeakNearPoweredGeneratorCreatesIgnitionForecast() { - var level = LevelState.Create("Fuel leak", 6, 6).SetCell(new(2, 2), new() - { - Kind = CellKind.Generator, - Pipe = PipeMedium.Fuel, - LeakRate = 4, - Pressure = 8, - Integrity = 8, - Powered = true - }); + var level = LevelState.Create("Fuel leak", 6, 6) + .SetCell(new(2, 2), new() { + Kind = ECellKind.Generator, + Pipe = EPipeMedium.Fuel, + LeakRate = 4, + Pressure = 8, + Integrity = 8, + Powered = true + }); - var forecasts = _engine.Forecast(level); + var forecasts = m_Engine.Forecast(level); - Assert.Contains(forecasts, forecast => forecast.Kind == FailureKind.Ignition && forecast.Position == new GridPosition(2, 2)); + Assert.Contains(forecasts, forecast => forecast.Kind == EFailureKind.Ignition && forecast.Position == new GridPosition(2, 2)); } [Fact] public void CoolantLeakOnPoweredCellRaisesElectricalCharge() { - var level = LevelState.Create("Wet cable", 6, 6).SetCell(new(3, 3), new() - { - Pipe = PipeMedium.Coolant, - LeakRate = 3, - Powered = true - }); + var level = LevelState.Create("Wet cable", 6, 6) + .SetCell(new(3, 3), new() { + Pipe = EPipeMedium.Coolant, + LeakRate = 3, + Powered = true + }); - var next = _engine.AdvanceTurn(level); + var next = m_Engine.AdvanceTurn(level); Assert.True(next.GetCell(new(3, 3)).Hazards.ElectricalCharge >= 2); } @@ -38,37 +38,37 @@ public sealed class SimulationEngineTests [Fact] public void OverpressurePredictsPipeBurst() { - var level = LevelState.Create("Pressure", 6, 6).SetCell(new(1, 2), new() - { - Pipe = PipeMedium.Pressure, - Pressure = 10, - Integrity = 6 - }); + var level = LevelState.Create("Pressure", 6, 6) + .SetCell(new(1, 2), new() { + Pipe = EPipeMedium.Pressure, + Pressure = 10, + Integrity = 6 + }); - var forecasts = _engine.Forecast(level); + var forecasts = m_Engine.Forecast(level); - Assert.Contains(forecasts, forecast => forecast.Kind == FailureKind.PipeBurst && forecast.Turns == 2); + Assert.Contains(forecasts, forecast => forecast.Kind == EFailureKind.PipeBurst && forecast.Turns == 2); } [Fact] public void StableReactorWithPowerAndCoolingCanActivate() { - var level = LevelState.Create("Ready", 8, 6).SetCell(new(2, 2), new() - { - Kind = CellKind.Reactor, - Hazards = new() { Heat = 3 } - }).SetCell(new(3, 2), new() - { - Kind = CellKind.Generator, - Powered = true - }).SetCell(new(4, 2), new() - { - Kind = CellKind.CoolingPump, - Powered = true - }); + var level = LevelState.Create("Ready", 8, 6) + .SetCell(new(2, 2), new() { + Kind = ECellKind.Reactor, + Hazards = new() { Heat = 3 } + }) + .SetCell(new(3, 2), new() { + Kind = ECellKind.Generator, + Powered = true + }) + .SetCell(new(4, 2), new() { + Kind = ECellKind.CoolingPump, + Powered = true + }); - var next = _engine.AdvanceTurn(level); - var activated = _engine.ActivateReactor(next); + var next = m_Engine.AdvanceTurn(level); + var activated = m_Engine.ActivateReactor(next); Assert.Equal("REACTOR ONLINE", activated.Global.Status); Assert.True(activated.Global.ReactorActivated); @@ -78,18 +78,18 @@ public sealed class SimulationEngineTests public void LevelSerializationRoundTripsEditableState() { var level = LevelState.Create("Round trip", 5, 5); - level = LevelEditor.Apply(level, new(2, 2), EditorTool.Reactor); - level = LevelEditor.Apply(level, new(1, 2), EditorTool.CoolantPipe); - level = LevelEditor.Apply(level, new(1, 2), EditorTool.Leak); + level = LevelEditor.Apply(level, new(2, 2), EEditorTool.Reactor); + level = LevelEditor.Apply(level, new(1, 2), EEditorTool.CoolantPipe); + level = LevelEditor.Apply(level, new(1, 2), EEditorTool.Leak); var json = LevelSerializer.Serialize(level); var loaded = LevelSerializer.Deserialize(json); Assert.Equal(level.Name, loaded.Name); - Assert.Equal(CellKind.Reactor, loaded.GetCell(new(2, 2)).Kind); - Assert.Equal(PipeMedium.Coolant, loaded.GetCell(new(1, 2)).Pipe); + Assert.Equal(ECellKind.Reactor, loaded.GetCell(new(2, 2)).Kind); + Assert.Equal(EPipeMedium.Coolant, loaded.GetCell(new(1, 2)).Pipe); Assert.Equal(1, loaded.GetCell(new(1, 2)).LeakRate); } - private readonly SimulationEngine _engine = new(); + private readonly SimulationEngine m_Engine = new(); } \ No newline at end of file