diff --git a/.editorconfig b/.editorconfig
index 40d50bd70..0f6ad48a5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -43,31 +43,31 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
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.interface.required_modifiers =
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.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.non_field_members.required_modifiers =
# Naming styles
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.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
-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.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.pascal_case.required_prefix =
-dotnet_naming_style.pascal_case.required_suffix =
-dotnet_naming_style.pascal_case.word_separator =
+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_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
@@ -81,7 +81,97 @@ dotnet_style_prefer_auto_properties = true:silent
[*]
# Microsoft .NET properties
+csharp_new_line_before_members_in_object_initializers = false
+csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
+csharp_prefer_braces = false:suggestion
csharp_preserve_single_line_blocks = true
+csharp_style_expression_bodied_accessors = true:suggestion
+csharp_style_expression_bodied_constructors = true:none
+csharp_style_expression_bodied_methods = true:suggestion
+csharp_style_expression_bodied_properties = true:suggestion
+csharp_style_namespace_declarations = block_scoped:none
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_style_var_for_built_in_types = true:suggestion
+dotnet_naming_rule.constants_rule.import_to_resharper = as_predefined
+dotnet_naming_rule.constants_rule.severity = suggestion
+dotnet_naming_rule.constants_rule.style = pascal_case
+dotnet_naming_rule.constants_rule.symbols = constants_symbols
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.import_to_resharper = True
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.resharper_description = interface_should_be_begins_with_i
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.resharper_guid = 336fd165-eb04-40c2-ae13-5363789422ca
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.style = begins_with_i
+dotnet_naming_rule.interface_should_be_begins_with_i_rule.symbols = interface_should_be_begins_with_i_symbols
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.import_to_resharper = True
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.resharper_description = non_field_members_should_be_pascal_case
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.resharper_guid = bfe4a45b-02bb-4987-9b46-2c7a34bb09e8
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.resharper_style = AaBb, aaBb
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.style = pascal_case
+dotnet_naming_rule.non_field_members_should_be_pascal_case_rule.symbols = non_field_members_should_be_pascal_case_symbols
+dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined
+dotnet_naming_rule.private_constants_rule.severity = suggestion
+dotnet_naming_rule.private_constants_rule.style = pascal_case
+dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols
+dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined
+dotnet_naming_rule.private_static_readonly_rule.resharper_style = AaBb, _ + aaBb
+dotnet_naming_rule.private_static_readonly_rule.severity = suggestion
+dotnet_naming_rule.private_static_readonly_rule.style = pascal_case
+dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols
+dotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined
+dotnet_naming_rule.public_fields_rule.resharper_style = AaBb, aaBb
+dotnet_naming_rule.public_fields_rule.severity = suggestion
+dotnet_naming_rule.public_fields_rule.style = pascal_case
+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 = suggestion
+dotnet_naming_rule.static_readonly_rule.style = pascal_case
+dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
+dotnet_naming_rule.types_should_be_pascal_case_rule.import_to_resharper = True
+dotnet_naming_rule.types_should_be_pascal_case_rule.resharper_description = types_should_be_pascal_case
+dotnet_naming_rule.types_should_be_pascal_case_rule.resharper_guid = 46acfd3a-c646-4b13-9908-9d4a8e3bc8ac
+dotnet_naming_rule.types_should_be_pascal_case_rule.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case_rule.style = pascal_case
+dotnet_naming_rule.types_should_be_pascal_case_rule.symbols = types_should_be_pascal_case_symbols
+dotnet_naming_style.i_upper_camel_case_style.capitalization = pascal_case
+dotnet_naming_style.i_upper_camel_case_style.required_prefix = I
+dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
+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.interface_should_be_begins_with_i_symbols.applicable_accessibilities = local, public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface_should_be_begins_with_i_symbols.applicable_kinds = interface
+dotnet_naming_symbols.interface_should_be_begins_with_i_symbols.resharper_applicable_kinds = interface
+dotnet_naming_symbols.interface_should_be_begins_with_i_symbols.resharper_required_modifiers = any
+dotnet_naming_symbols.non_field_members_should_be_pascal_case_symbols.applicable_accessibilities = local, public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members_should_be_pascal_case_symbols.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members_should_be_pascal_case_symbols.resharper_applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members_should_be_pascal_case_symbols.resharper_required_modifiers = any
+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_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.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_should_be_pascal_case_symbols.applicable_accessibilities = local, public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_should_be_pascal_case_symbols.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types_should_be_pascal_case_symbols.resharper_applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types_should_be_pascal_case_symbols.resharper_required_modifiers = any
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+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
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ReSharper properties
resharper_align_first_arg_by_paren = false
@@ -97,6 +187,7 @@ resharper_align_multline_type_parameter_constrains = true
resharper_align_multline_type_parameter_list = true
resharper_align_tuple_components = true
resharper_allow_comment_after_lbrace = true
+resharper_blank_lines_after_block_statements = 0
resharper_blank_lines_around_single_line_type = 0
resharper_constructor_or_destructor_body = expression_body
resharper_csharp_align_first_arg_by_paren = false
@@ -106,11 +197,13 @@ resharper_csharp_empty_block_style = together
resharper_csharp_indent_invocation_pars = outside_and_inside
resharper_csharp_indent_method_decl_pars = outside_and_inside
resharper_csharp_indent_statement_pars = outside
+resharper_csharp_naming_rule.other = AaBb, aaBb
resharper_csharp_stick_comment = false
resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_before_binary_opsign = true
resharper_csharp_wrap_lines = false
resharper_csharp_wrap_parameters_style = chop_if_long
+resharper_enforce_line_ending_style = true
resharper_indent_anonymous_method_block = false
resharper_indent_nested_fixed_stmt = true
resharper_indent_nested_foreach_stmt = true
@@ -131,11 +224,39 @@ resharper_nested_ternary_style = expanded
resharper_outdent_statement_labels = true
resharper_place_linq_into_on_new_line = false
resharper_place_simple_case_statement_on_same_line = true
+resharper_show_autodetect_configure_formatting_tip = false
+resharper_use_indent_from_vs = false
resharper_wrap_chained_binary_expressions = chop_if_long
resharper_wrap_chained_binary_patterns = chop_if_long
-csharp_new_line_before_else = true
+csharp_new_line_before_else = false
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
-csharp_new_line_before_catch = true
+csharp_new_line_before_catch = false
csharp_new_line_before_open_brace = none
+
+# Standard properties
+end_of_line = crlf
+
+# ReSharper inspection severities
+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_check_namespace_highlighting = none
+resharper_inconsistent_naming_highlighting = suggestion
+resharper_redundant_base_qualifier_highlighting = warning
+resharper_suggest_var_or_type_built_in_types_highlighting = hint
+resharper_suggest_var_or_type_elsewhere_highlighting = none
+resharper_suggest_var_or_type_simple_types_highlighting = none
+csharp_style_prefer_primary_constructors = true:suggestion
+
+[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cs,cshtml,csproj,cu,cuh,cxx,dbml,discomap,dtd,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,ixx,jsproj,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,razor,resw,resx,skin,StyleCop,targets,tasks,tpp,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}]
+indent_style = space
+indent_size = 4
+tab_width = 4
+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
diff --git a/.github/ISSUE_TEMPLATE/Broken-Feature.yml b/.github/ISSUE_TEMPLATE/Broken-Feature.yml
new file mode 100644
index 000000000..50962dc47
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Broken-Feature.yml
@@ -0,0 +1,106 @@
+name: Broken Feature
+description: Create a report if one of Toybox's features doesn't work
+title: "
"
+labels: ["bug", "broken feature"]
+body:
+- type: textarea
+ id: bugdescription
+ attributes:
+ label: Description of the bug
+ description: Please give a clear and consise description of the bug you are experiencing
+ validations:
+ required: true
+- type: textarea
+ id: reproductionsteps
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. Go to...
+ 2. Click on...
+ 3. Scroll down to...
+ 4. See error...
+ validations:
+ required: true
+- type: textarea
+ id: expectedbehavior
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: false
+- type: textarea
+ id: logfiles
+ attributes:
+ label: Log Files
+ description: Find your log files from the FAQ in the wiki
+ placeholder: Drag and drop your log files here!
+ validations:
+ required: false
+- type: textarea
+ id: savefile
+ attributes:
+ label: Save File
+ description: Zip your named save file (not an auto save!)
+ placeholder: Drag and drop your zipped save file here!
+ validations:
+ required: false
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: Screenshot of the bug
+ placeholder: Drag and drop screenshots of the bug here!
+ validations:
+ required: false
+- type: textarea
+ id: settings
+ attributes:
+ label: Settings
+ description: Please provide your settings file
+ placeholder: Drag and drop your settings.xml file here!
+ validations:
+ required: false
+- type: input
+ id: toyboxversion
+ attributes:
+ label: Toybox Version
+ description: Please provide the version of Toybox you are using
+ placeholder: Example - 1.5.4c
+ validations:
+ required: true
+- type: input
+ id: os
+ attributes:
+ label: Operating System
+ description: Please provide what operating system you use
+ placeholder: Examples - Windows, MacOS
+ validations:
+ required: false
+- type: dropdown
+ id: whatgame
+ attributes:
+ label: What game are you using Toybox on?
+ options:
+ - Pathfinder Wrath of the Righteous
+ - Rogue Trader
+ validations:
+ required: true
+- type: input
+ id: gameversion
+ attributes:
+ label: Game Version
+ description: Please provide what version of the game you are on
+ placeholder: Example - 2.1.4w
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/Broken-Gameplay.yml b/.github/ISSUE_TEMPLATE/Broken-Gameplay.yml
new file mode 100644
index 000000000..dbc5baf88
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Broken-Gameplay.yml
@@ -0,0 +1,106 @@
+name: Broken Gameplay
+description: Create a report if Toybox is breaking gameplay while enabled or a feature is turned on
+title: ""
+labels: ["bug", "broken gameplay"]
+body:
+- type: textarea
+ id: bugdescription
+ attributes:
+ label: Description of the bug
+ description: Please give a clear and consise description of the bug you are experiencing
+ validations:
+ required: true
+- type: textarea
+ id: reproductionsteps
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. Go to...
+ 2. Click on...
+ 3. Scroll down to...
+ 4. See error...
+ validations:
+ required: true
+- type: textarea
+ id: expectedbehavior
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: false
+- type: textarea
+ id: logfiles
+ attributes:
+ label: Log Files
+ description: Find your log files from the FAQ in the wiki
+ placeholder: Drag and drop your log files here!
+ validations:
+ required: false
+- type: textarea
+ id: savefile
+ attributes:
+ label: Save File
+ description: Zip your named save file (not an auto save!)
+ placeholder: Drag and drop your zipped save file here!
+ validations:
+ required: false
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: Screenshot of the bug
+ placeholder: Drag and drop screenshots of the bug here!
+ validations:
+ required: false
+- type: textarea
+ id: settings
+ attributes:
+ label: Settings
+ description: Please provide your settings file
+ placeholder: Drag and drop your settings.xml file here!
+ validations:
+ required: false
+- type: input
+ id: toyboxversion
+ attributes:
+ label: Toybox Version
+ description: Please provide the version of Toybox you are using
+ placeholder: Example - 1.5.4c
+ validations:
+ required: true
+- type: input
+ id: os
+ attributes:
+ label: Operating System
+ description: Please provide what operating system you use
+ placeholder: Examples - Windows, MacOS
+ validations:
+ required: false
+- type: dropdown
+ id: whatgame
+ attributes:
+ label: What game are you using Toybox on?
+ options:
+ - Pathfinder Wrath of the Righteous
+ - Rogue Trader
+ validations:
+ required: true
+- type: input
+ id: gameversion
+ attributes:
+ label: Game Version
+ description: Please provide what version of the game you are on
+ placeholder: Example - 2.1.4w
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/Crashes.yml b/.github/ISSUE_TEMPLATE/Crashes.yml
new file mode 100644
index 000000000..d927748ea
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Crashes.yml
@@ -0,0 +1,106 @@
+name: Crashes or Data Loss
+description: Create a report if Toybox is crashing or lossing data
+title: ""
+labels: ["bug", "crashes"]
+body:
+- type: textarea
+ id: bugdescription
+ attributes:
+ label: Description of the bug
+ description: Please give a clear and consise description of the bug you are experiencing
+ validations:
+ required: true
+- type: textarea
+ id: reproductionsteps
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. Go to...
+ 2. Click on...
+ 3. Scroll down to...
+ 4. See error...
+ validations:
+ required: true
+- type: textarea
+ id: expectedbehavior
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: false
+- type: textarea
+ id: logfiles
+ attributes:
+ label: Log Files
+ description: Find your log files from the FAQ in the wiki
+ placeholder: Drag and drop your log files here!
+ validations:
+ required: false
+- type: textarea
+ id: savefile
+ attributes:
+ label: Save File
+ description: Zip your named save file (not an auto save!)
+ placeholder: Drag and drop your zipped save file here!
+ validations:
+ required: false
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: Screenshot of the bug
+ placeholder: Drag and drop screenshots of the bug here!
+ validations:
+ required: false
+- type: textarea
+ id: settings
+ attributes:
+ label: Settings
+ description: Please provide your settings file
+ placeholder: Drag and drop your settings.xml file here!
+ validations:
+ required: false
+- type: input
+ id: toyboxversion
+ attributes:
+ label: Toybox Version
+ description: Please provide the version of Toybox you are using
+ placeholder: Example - 1.5.4c
+ validations:
+ required: true
+- type: input
+ id: os
+ attributes:
+ label: Operating System
+ description: Please provide what operating system you use
+ placeholder: Examples - Windows, MacOS
+ validations:
+ required: false
+- type: dropdown
+ id: whatgame
+ attributes:
+ label: What game are you using Toybox on?
+ options:
+ - Pathfinder Wrath of the Righteous
+ - Rogue Trader
+ validations:
+ required: true
+- type: input
+ id: gameversion
+ attributes:
+ label: Game Version
+ description: Please provide what version of the game you are on
+ placeholder: Example - 2.1.4w
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/Gestalt-Issue.yml b/.github/ISSUE_TEMPLATE/Gestalt-Issue.yml
new file mode 100644
index 000000000..9f15d10b2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Gestalt-Issue.yml
@@ -0,0 +1,106 @@
+name: Gestalt Issue
+description: Create a report if you are having issues with Toyboxs Gestalt feature
+title: ""
+labels: ["bug", "gestalt-bugs"]
+body:
+- type: textarea
+ id: bugdescription
+ attributes:
+ label: Description of the bug
+ description: Please give a clear and consise description of the bug you are experiencing
+ validations:
+ required: true
+- type: textarea
+ id: reproductionsteps
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. Go to...
+ 2. Click on...
+ 3. Scroll down to...
+ 4. See error...
+ validations:
+ required: true
+- type: textarea
+ id: expectedbehavior
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: false
+- type: textarea
+ id: logfiles
+ attributes:
+ label: Log Files
+ description: Find your log files from the FAQ in the wiki
+ placeholder: Drag and drop your log files here!
+ validations:
+ required: false
+- type: textarea
+ id: savefile
+ attributes:
+ label: Save File
+ description: Zip your named save file (not an auto save!)
+ placeholder: Drag and drop your zipped save file here!
+ validations:
+ required: false
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: Screenshot of the bug
+ placeholder: Drag and drop screenshots of the bug here!
+ validations:
+ required: false
+- type: textarea
+ id: settings
+ attributes:
+ label: Settings
+ description: Please provide your settings file
+ placeholder: Drag and drop your settings.xml file here!
+ validations:
+ required: false
+- type: input
+ id: toyboxversion
+ attributes:
+ label: Toybox Version
+ description: Please provide the version of Toybox you are using
+ placeholder: Example - 1.5.4c
+ validations:
+ required: true
+- type: input
+ id: os
+ attributes:
+ label: Operating System
+ description: Please provide what operating system you use
+ placeholder: Examples - Windows, MacOS
+ validations:
+ required: false
+- type: dropdown
+ id: whatgame
+ attributes:
+ label: What game are you using Toybox on?
+ options:
+ - Pathfinder Wrath of the Righteous
+ - Rogue Trader
+ validations:
+ required: true
+- type: input
+ id: gameversion
+ attributes:
+ label: Game Version
+ description: Please provide what version of the game you are on
+ placeholder: Example - 2.1.4w
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/UI-Issue.yml b/.github/ISSUE_TEMPLATE/UI-Issue.yml
new file mode 100644
index 000000000..992388c5e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/UI-Issue.yml
@@ -0,0 +1,106 @@
+name: UI Issue
+description: Create a report if Toybox UI is messed up
+title: ""
+labels: ["bug", "ui issues"]
+body:
+- type: textarea
+ id: bugdescription
+ attributes:
+ label: Description of the bug
+ description: Please give a clear and consise description of the bug you are experiencing
+ validations:
+ required: true
+- type: textarea
+ id: reproductionsteps
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. Go to...
+ 2. Click on...
+ 3. Scroll down to...
+ 4. See error...
+ validations:
+ required: true
+- type: textarea
+ id: expectedbehavior
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: false
+- type: textarea
+ id: logfiles
+ attributes:
+ label: Log Files
+ description: Find your log files from the FAQ in the wiki
+ placeholder: Drag and drop your log files here!
+ validations:
+ required: false
+- type: textarea
+ id: savefile
+ attributes:
+ label: Save File
+ description: Zip your named save file (not an auto save!)
+ placeholder: Drag and drop your zipped save file here!
+ validations:
+ required: false
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: Screenshot of the bug
+ placeholder: Drag and drop screenshots of the bug here!
+ validations:
+ required: true
+- type: textarea
+ id: settings
+ attributes:
+ label: Settings
+ description: Please provide your settings file
+ placeholder: Drag and drop your settings.xml file here!
+ validations:
+ required: false
+- type: input
+ id: toyboxversion
+ attributes:
+ label: Toybox Version
+ description: Please provide the version of Toybox you are using
+ placeholder: Example - 1.5.4c
+ validations:
+ required: true
+- type: input
+ id: os
+ attributes:
+ label: Operating System
+ description: Please provide what operating system you use
+ placeholder: Examples - Windows, MacOS
+ validations:
+ required: false
+- type: dropdown
+ id: whatgame
+ attributes:
+ label: What game are you using Toybox on?
+ options:
+ - Pathfinder Wrath of the Righteous
+ - Rogue Trader
+ validations:
+ required: true
+- type: input
+ id: gameversion
+ attributes:
+ label: Game Version
+ description: Please provide what version of the game you are on
+ placeholder: Example - 2.1.4w
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+
+ Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 40481fe7f..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,39 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Save Files**
-To help us reproduce your issue, please attach a named *save file*
-Please zip it or GitHub will not accept it.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Settings**
-Please attach your settings.xml file from the mod folder
-
-**Version Info:**
- - ToyBox Version: [e.g. 1.3.4]
- - OS: [e.g. iOS]
- - Version [e.g. 22]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..29ad29f14
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Discord Community
+ url: https://discord.com/invite/owlcat
+ about: Join mod-user-general in the Discord for further support
diff --git a/.github/ISSUE_TEMPLATE/enhancement-request.md b/.github/ISSUE_TEMPLATE/enhancement-request.md
deleted file mode 100644
index b764e3299..000000000
--- a/.github/ISSUE_TEMPLATE/enhancement-request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Enhancement Request
-about: Suggest an idea for this project
-title: ''
-labels: enhancement
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml
new file mode 100644
index 000000000..99427c34c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/enhancement.yml
@@ -0,0 +1,33 @@
+name: Enhancement Request
+description: Suggest an idea for this project
+title: ''
+labels: enhancement
+body:
+- type: textarea
+ id: relatedtobug
+ attributes:
+ label: Is your feature request related to a problem?
+ description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+ validations:
+ required: true
+- type: textarea
+ id: proposedsolution
+ attributes:
+ label: Describe the solution you'd like
+ description: A clear and concise description of what you want to happen
+ validations:
+ required: true
+- type: textarea
+ id: altsolutions
+ attributes:
+ label: Describe alternatives you've considered
+ description: A clear and concise description of any alternative solutions or features you've considered
+ validations:
+ required: false
+- type: textarea
+ id: extrainfo
+ attributes:
+ label: Additional context
+ description: Add any other context or screenshots about the feature request here
+ validations:
+ required: false
diff --git a/.github/workflows/Build Mod on PR.yml b/.github/workflows/Build Mod on PR.yml
new file mode 100644
index 000000000..a09838533
--- /dev/null
+++ b/.github/workflows/Build Mod on PR.yml
@@ -0,0 +1,39 @@
+name: Build Mod on PR
+
+on:
+ pull_request_target:
+ types: [labeled]
+
+jobs:
+
+ build:
+ if: ${{ contains(github.event.pull_request.labels.*.name, 'safe to test') }}
+ runs-on: windows-latest
+
+ steps:
+ - name: Build
+ id: build-action
+ uses: xADDBx/BuildOwlcatMod@v2
+ with:
+ GAME_NAME: RogueTrader
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_NAME: ${{ github.repository_owner }}
+ PACKAGE_OWNER: xADDBx
+ BRANCH_REF: ${{ github.event.pull_request.head.sha }}
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.zipFile }}
+ path: ${{ env.outDir }}
+
+ - name: Remove Test Label
+ if: always()
+ shell: bash
+ run: |
+ curl --silent --fail-with-body \
+ -X DELETE \
+ -H "Accept: application/vnd.github+json" \
+ -H "Authorization: Bearer ${{ secrets.WRITE_PR_TOKEN }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ 'https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.number }}/labels/safe%20to%20test'
diff --git a/.github/workflows/Build Mod on Push.yml b/.github/workflows/Build Mod on Push.yml
new file mode 100644
index 000000000..aa4b3a72a
--- /dev/null
+++ b/.github/workflows/Build Mod on Push.yml
@@ -0,0 +1,26 @@
+name: Build Mod on Push
+
+on:
+ push:
+
+jobs:
+
+ build:
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ runs-on: windows-latest
+
+ steps:
+ - name: Build
+ id: build-action
+ uses: xADDBx/BuildOwlcatMod@v2
+ with:
+ GAME_NAME: RogueTrader
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_NAME: ${{ github.repository_owner }}
+ PACKAGE_OWNER: xADDBx
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.zipFile }}
+ path: ${{ env.outDir }}
diff --git a/.github/workflows/Create Release for new Tag.yml b/.github/workflows/Create Release for new Tag.yml
new file mode 100644
index 000000000..3132581cb
--- /dev/null
+++ b/.github/workflows/Create Release for new Tag.yml
@@ -0,0 +1,36 @@
+name: Create Release for new Tag
+
+on:
+ push:
+ tags:
+ - '**'
+
+jobs:
+
+ build:
+ runs-on: windows-latest
+ permissions:
+ contents: write
+ packages: read
+
+ steps:
+ - name: Build
+ id: build-action
+ uses: xADDBx/BuildOwlcatMod@v2
+ with:
+ GAME_NAME: RogueTrader
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_NAME: ${{ github.repository_owner }}
+ PACKAGE_OWNER: xADDBx
+
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.zipFile }}
+ path: ${{ env.outDir }}
+
+ - name: Create Release
+ uses: ncipollo/release-action@v1
+ with:
+ artifacts: ${{ env.zipFilePath }}\*.zip
+ name: ${{ env.ZipFile }} built for Rogue Trader ${{ env.gameVersionNum }}${{ env.gameVersionSuffix }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 85976aba6..154613e12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
+GamePath.props
# Rider
.idea/
@@ -368,4 +368,5 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
-FodyWeavers.xsd
\ No newline at end of file
+FodyWeavers.xsd
+/GitDiff
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index 7b5392e14..000000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- 10.0
- v6.0
-
-
\ No newline at end of file
diff --git a/ModKit/DataViewer/ObjectSet.cs b/ModKit/DataViewer/ObjectSet.cs
deleted file mode 100644
index 3cff6a3a0..000000000
--- a/ModKit/DataViewer/ObjectSet.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.Serialization;
-using System.Collections.Generic;
-
-
-namespace ModKit.DataViewer {
- public interface IObjectSet {
- /// check the existence of an object.
- /// true if object is exist, false otherwise.
- bool IsExist(object obj);
-
- /// if the object is not in the set, add it in. else do nothing.
- /// true if successfully added, false otherwise.
- bool Add(object obj);
- }
-
- public sealed class ObjectSetUsingConditionalWeakTable : IObjectSet {
- /// unit test on object set.
- internal static void Main() {
- Stopwatch sw = new Stopwatch();
- sw.Start();
- ObjectSetUsingConditionalWeakTable objSet = new ObjectSetUsingConditionalWeakTable();
- for (int i = 0; i < 10000000; ++i) {
- object obj = new object();
- if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
- if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
- if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
- }
- sw.Stop();
- Console.WriteLine(sw.ElapsedMilliseconds);
- }
-
- public bool IsExist(object obj) {
- return objectSet.TryGetValue(obj, out tryGetValue_out0);
- }
-
- public bool Add(object obj) {
- if (IsExist(obj)) {
- return false;
- }
- else {
- objectSet.Add(obj, null);
- return true;
- }
- }
-
- /// internal representation of the set. (only use the key)
- private ConditionalWeakTable objectSet = new ConditionalWeakTable();
-
- /// used to fill the out parameter of ConditionalWeakTable.TryGetValue().
- private static object tryGetValue_out0 = null;
- }
-
- [Obsolete("It will crash if there are too many objects and ObjectSetUsingConditionalWeakTable get a better performance.")]
- public sealed class ObjectSetUsingObjectIDGenerator : IObjectSet {
- /// unit test on object set.
- internal static void Main() {
- Stopwatch sw = new Stopwatch();
- sw.Start();
- ObjectSetUsingObjectIDGenerator objSet = new ObjectSetUsingObjectIDGenerator();
- for (int i = 0; i < 10000000; ++i) {
- object obj = new object();
- if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
- if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
- if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
- }
- sw.Stop();
- Console.WriteLine(sw.ElapsedMilliseconds);
- }
-
-
- public bool IsExist(object obj) {
- bool firstTime;
- idGenerator.HasId(obj, out firstTime);
- return !firstTime;
- }
-
- public bool Add(object obj) {
- bool firstTime;
- idGenerator.GetId(obj, out firstTime);
- return firstTime;
- }
-
-
- /// internal representation of the set.
- private ObjectIDGenerator idGenerator = new ObjectIDGenerator();
- }
-}
diff --git a/ModKit/DataViewer/UnsafeForceCast.cs b/ModKit/DataViewer/UnsafeForceCast.cs
deleted file mode 100644
index 2efdad150..000000000
--- a/ModKit/DataViewer/UnsafeForceCast.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using ModKit.Utility;
-using System;
-using System.Reflection.Emit;
-
-namespace ModKit.DataViewer {
- internal static class UnsafeForceCast
- {
- private static readonly DoubleDictionary _cache = new DoubleDictionary();
-
- public static Func GetDelegate()
- {
- Func cache = default;
- if (_cache.TryGetValue(typeof(TInput), typeof(TOutput), out WeakReference weakRef))
- cache = weakRef.Target as Func;
- if (cache == null)
- {
- cache = CreateDelegate();
- _cache[typeof(TInput), typeof(TOutput)] = new WeakReference(cache);
- }
- return cache;
- }
-
- private static Func CreateDelegate()
- {
- DynamicMethod method = new DynamicMethod(
- name: "UnsafeForceCast",
- returnType: typeof(TOutput),
- parameterTypes: new[] { typeof(TInput) });
-
- ILGenerator il = method.GetILGenerator();
- il.Emit(OpCodes.Ldarg_0);
- if (typeof(TInput) == typeof(object) && typeof(TOutput).IsValueType)
- il.Emit(OpCodes.Unbox_Any, typeof(TOutput));
- il.Emit(OpCodes.Ret);
-
- return method.CreateDelegate(typeof(Func)) as Func;
- }
- }
-}
diff --git a/ModKit/ModKit.csproj b/ModKit/ModKit.csproj
deleted file mode 100644
index 3434e42aa..000000000
--- a/ModKit/ModKit.csproj
+++ /dev/null
@@ -1,151 +0,0 @@
-
-
-
- net4.7.2
- ModKit
- ModKit
- Narria
- Cabarius
- ModKit
- A game agnostic toolkit for building Mod UI for unity mod manager based mods. Builds on ModMaker (https://github.com/cabarius/WrathModMaker and all past repos it was forked from)
- Copyright © 2021
- LICENSE.txt
-
- https://github.com/cabarius/ToyBox
- Unity, Mod, UnityModManager, UMM
-
-
-
- 5
- true
- 9
-
-
-
-
- True
-
-
-
- True
-
-
-
- True
-
-
-
- True
- ModKit
-
-
- True
- UI
-
-
- True
- Utility
-
-
-
-
-
-
-
-
-
-
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.AssetBundleModule.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.CoreModule.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.IMGUIModule.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.InputLegacyModule.dll
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.InputModule.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.TextRenderingModule.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityEngine.UI.dll
- false
-
-
- $(WrathPath)\Wrath_Data\Managed\UnityModManager\UnityModManager.dll
- false
-
-
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
-
-
- false
-
-
-
diff --git a/ModKit/ModKit.sln b/ModKit/ModKit.sln
deleted file mode 100644
index 5c7b16169..000000000
--- a/ModKit/ModKit.sln
+++ /dev/null
@@ -1,30 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31205.134
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModKit", "C:\Users\PC\source\repos\ToyBox\ModKit\ModKit.csproj", "{BD28D55C-3A8D-4078-9C56-D8FA20526C7E}"
-EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ModKitSrc", "ModKitSrc.shproj", "{7D3F9428-C134-47FE-9D04-305B4D330401}"
-EndProject
-Global
- GlobalSection(SharedMSBuildProjectFiles) = preSolution
- ModKitSrc.projitems*{7d3f9428-c134-47fe-9d04-305b4d330401}*SharedItemsImports = 13
- ModKitSrc.projitems*{bd28d55c-3a8d-4078-9c56-d8fa20526c7e}*SharedItemsImports = 5
- EndGlobalSection
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {BD28D55C-3A8D-4078-9C56-D8FA20526C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BD28D55C-3A8D-4078-9C56-D8FA20526C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BD28D55C-3A8D-4078-9C56-D8FA20526C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BD28D55C-3A8D-4078-9C56-D8FA20526C7E}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {2F8600CB-2139-46EF-B0F5-36C36F060BAD}
- EndGlobalSection
-EndGlobal
diff --git a/ModKit/ModKit/MenuManager.cs b/ModKit/ModKit/MenuManager.cs
deleted file mode 100644
index 4c660ace3..000000000
--- a/ModKit/ModKit/MenuManager.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using UnityEngine;
-using UnityModManagerNet;
-using ModKit.Utility;
-
-namespace ModKit {
- public interface IMenuPage {
- string Name { get; }
-
- int Priority { get; }
-
- void OnGUI(UnityModManager.ModEntry modEntry);
- }
-
- public interface IMenuTopPage : IMenuPage { }
-
- public interface IMenuSelectablePage : IMenuPage { }
-
- public interface IMenuBottomPage : IMenuPage { }
-
- public class MenuManager : INotifyPropertyChanged {
- public event PropertyChangedEventHandler PropertyChanged;
-
- // This method is called by the Set accessor of each property.
- // The CallerMemberName attribute that is applied to the optional propertyName
- // parameter causes the property name of the caller to be substituted as an argument.
- private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
-
- #region Fields
- private int _tabIndex;
- public int tabIndex {
- get { return _tabIndex; }
- set { _tabIndex = value; NotifyPropertyChanged(); }
- }
- private readonly List _topPages = new();
- private readonly List _selectablePages = new();
- private readonly List _bottomPages = new();
- private static Exception caughtException = null;
-
- #endregion
-
- #region Toggle
-
- public void Enable(UnityModManager.ModEntry modEntry, Assembly _assembly) {
- foreach (var type in _assembly.GetTypes()
- .Where(type => !type.IsInterface && !type.IsAbstract && typeof(IMenuPage).IsAssignableFrom(type))) {
- if (typeof(IMenuTopPage).IsAssignableFrom(type))
- _topPages.Add(Activator.CreateInstance(type, true) as IMenuTopPage);
-
- if (typeof(IMenuSelectablePage).IsAssignableFrom(type))
- _selectablePages.Add(Activator.CreateInstance(type, true) as IMenuSelectablePage);
-
- if (typeof(IMenuBottomPage).IsAssignableFrom(type))
- _bottomPages.Add(Activator.CreateInstance(type, true) as IMenuBottomPage);
- }
-
- static int comparison(IMenuPage x, IMenuPage y) => x.Priority - y.Priority;
- _topPages.Sort(comparison);
- _selectablePages.Sort(comparison);
- _bottomPages.Sort(comparison);
-
- modEntry.OnGUI += OnGUI;
- }
-
- public void Disable(UnityModManager.ModEntry modEntry) {
- modEntry.OnGUI -= OnGUI;
-
- _topPages.Clear();
- _selectablePages.Clear();
- _bottomPages.Clear();
- }
-
- #endregion
-
- private void OnGUI(UnityModManager.ModEntry modEntry) {
- var hasPriorPage = false;
- try {
- if (caughtException != null) {
- GUILayout.Label("ERROR".Red().Bold() + $": caught exception {caughtException}");
- if (GUILayout.Button("Reset".Orange().Bold(), GUILayout.ExpandWidth(false))) {
- caughtException = null;
- }
- return;
- }
- var e = Event.current;
- UI.userHasHitReturn = e.keyCode == KeyCode.Return;
- UI.focusedControlName = GUI.GetNameOfFocusedControl();
-
-
- if (_topPages.Count > 0) {
- foreach (var page in _topPages) {
- if (hasPriorPage)
- GUILayout.Space(10f);
- page.OnGUI(modEntry);
- hasPriorPage = true;
- }
- }
-
- if (_selectablePages.Count > 0) {
- if (_selectablePages.Count > 1) {
- if (hasPriorPage)
- GUILayout.Space(10f);
- tabIndex = GUILayout.Toolbar(tabIndex, _selectablePages.Select(page => page.Name).ToArray());
-
- GUILayout.Space(10f);
- }
-
- _selectablePages[tabIndex].OnGUI(modEntry);
- hasPriorPage = true;
- }
-
- if (_bottomPages.Count > 0) {
- foreach (var page in _bottomPages) {
- if (hasPriorPage)
- GUILayout.Space(10f);
- page.OnGUI(modEntry);
- hasPriorPage = true;
- }
- }
- }
- catch (Exception e) {
- Console.Write($"{e}");
- caughtException = e;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/ModKit/ModKit/ModManager.cs b/ModKit/ModKit/ModManager.cs
deleted file mode 100644
index f00caafcb..000000000
--- a/ModKit/ModKit/ModManager.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-using HarmonyLib;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Reflection;
-using UnityModManagerNet;
-
-namespace ModKit {
- public interface IModEventHandler {
- int Priority { get; }
-
- void HandleModEnable();
-
- void HandleModDisable();
- }
-
- public partial class Mod {
- public delegate void ShowGUINotifierMethod();
- public static ShowGUINotifierMethod NotifyOnShowGUI;
-
- public static void OnShowGUI() {
- if (NotifyOnShowGUI != null) {
- NotifyOnShowGUI();
- }
- }
- }
-
- public class ModManager
- where TCore : class, new()
- where TSettings : UnityModManager.ModSettings, new() {
- #region Fields & Properties
-
- private UnityModManager.ModEntry.ModLogger _logger;
- private List _eventHandlers;
-
- public TCore Core { get; private set; }
-
- public TSettings Settings { get; private set; }
-
- public Version Version { get; private set; }
-
- public bool Enabled { get; private set; }
-
- public bool Patched { get; private set; }
-
- #endregion
-
- #region Toggle
-
- public void Enable(UnityModManager.ModEntry modEntry, Assembly assembly) {
- _logger = modEntry.Logger;
-
- if (Enabled) {
- Debug("Already enabled.");
- return;
- }
-
- using ProcessLogger process = new(_logger);
- try {
- Mod.modEntry = modEntry;
- process.Log("Enabling.");
- var dict = Harmony.VersionInfo(out var myVersion);
- process.Log($"Harmony version: {myVersion}");
- foreach (var entry in dict) {
- process.Log($"Mod {entry.Key} loaded with Harmony version {entry.Value}");
- }
-
- process.Log("Loading settings.");
- modEntry.OnSaveGUI += HandleSaveGUI;
- Version = modEntry.Version;
- Settings = UnityModManager.ModSettings.Load(modEntry);
- ModKitSettings.Load();
- Core = new TCore();
-
- var types = assembly.GetTypes();
-
- if (!Patched) {
- Harmony harmonyInstance = new(modEntry.Info.Id);
- foreach (var type in types) {
- var harmonyMethods = HarmonyMethodExtensions.GetFromType(type);
- if (harmonyMethods != null && harmonyMethods.Count() > 0) {
- process.Log($"Patching: {type.FullName}");
- try {
- var patchProcessor = harmonyInstance.CreateClassProcessor(type);
- patchProcessor.Patch();
- }
- catch (Exception e) {
- Error(e);
- }
- }
- }
- Patched = true;
- }
-
- Enabled = true;
-
- process.Log("Registering events.");
- _eventHandlers = types.Where(type => type != typeof(TCore) &&
- !type.IsInterface && !type.IsAbstract && typeof(IModEventHandler).IsAssignableFrom(type))
- .Select(type => Activator.CreateInstance(type, true) as IModEventHandler).ToList();
- if (Core is IModEventHandler core) {
- _eventHandlers.Add(core);
- }
- _eventHandlers.Sort((x, y) => x.Priority - y.Priority);
-
- process.Log("Raising events: OnEnable()");
- for (var i = 0; i < _eventHandlers.Count; i++) {
- _eventHandlers[i].HandleModEnable();
- }
- }
- catch (Exception e) {
- Error(e);
- Disable(modEntry, true);
- throw;
- }
-
- process.Log("Enabled.");
- }
-
- public void Disable(UnityModManager.ModEntry modEntry, bool unpatch = false) {
- _logger = modEntry.Logger;
-
- using ProcessLogger process = new(_logger);
- process.Log("Disabling.");
-
- Enabled = false;
-
- // use try-catch to prevent the progression being disrupt by exceptions
- if (_eventHandlers != null) {
- process.Log("Raising events: OnDisable()");
- for (var i = _eventHandlers.Count - 1; i >= 0; i--) {
- try { _eventHandlers[i].HandleModDisable(); }
- catch (Exception e) { Error(e); }
- }
- _eventHandlers = null;
- }
-
- if (unpatch) {
- Harmony harmonyInstance = new(modEntry.Info.Id);
- foreach (var method in harmonyInstance.GetPatchedMethods().ToList()) {
- var patchInfo = Harmony.GetPatchInfo(method);
- var patches =
- patchInfo.Transpilers.Concat(patchInfo.Postfixes).Concat(patchInfo.Prefixes)
- .Where(patch => patch.owner == modEntry.Info.Id);
- if (patches.Any()) {
- process.Log($"Unpatching: {patches.First().PatchMethod.DeclaringType.FullName} from {method.DeclaringType.FullName}.{method.Name}");
- foreach (var patch in patches) {
- try { harmonyInstance.Unpatch(method, patch.PatchMethod); }
- catch (Exception e) { Error(e); }
- }
- }
- }
- Patched = false;
- }
-
- modEntry.OnSaveGUI -= HandleSaveGUI;
- Core = null;
- Settings = null;
- Version = null;
- _logger = null;
-
- process.Log("Disabled.");
- }
-
- #endregion
-
- #region Settings
-
- public void ResetSettings() {
- if (Enabled) {
- Settings = new TSettings();
- Mod.ModKitSettings = new ModKitSettings();
- }
- }
-
- private void HandleSaveGUI(UnityModManager.ModEntry modEntry) {
- UnityModManager.ModSettings.Save(Settings, modEntry);
- ModKitSettings.Save();
- }
-
- #endregion
-
- #region Loggers
-
- public void Critical(string str) => _logger.Critical(str);
-
- public void Critical(object obj) => _logger.Critical(obj?.ToString() ?? "null");
-
- public void Error(Exception e) {
- _logger.Error($"{e.Message}\n{e.StackTrace}");
- if (e.InnerException != null)
- Error(e.InnerException);
- }
-
- public void Error(string str) => _logger.Error(str);
-
- public void Error(object obj) => _logger.Error(obj?.ToString() ?? "null");
-
- public void Log(string str) => _logger.Log(str);
-
- public void Log(object obj) => _logger.Log(obj?.ToString() ?? "null");
-
- public void Warning(string str) => _logger.Warning(str);
-
- public void Warning(object obj) => _logger.Warning(obj?.ToString() ?? "null");
-
- [Conditional("DEBUG")]
- public void Debug(MethodBase method, params object[] parameters) => _logger.Log($"{method.DeclaringType.Name}.{method.Name}({string.Join(", ", parameters)})");
-
- [Conditional("DEBUG")]
- public void Debug(string str) => _logger.Log(str);
-
- [Conditional("DEBUG")]
- public void Debug(object obj) => _logger.Log(obj?.ToString() ?? "null");
-
- #endregion
-
- private class ProcessLogger : IDisposable {
- private readonly Stopwatch _stopWatch = new();
- private readonly UnityModManager.ModEntry.ModLogger _logger;
-
- public ProcessLogger(UnityModManager.ModEntry.ModLogger logger) {
- _logger = logger;
- _stopWatch.Start();
- }
-
- public void Dispose() => _stopWatch.Stop();
-
- [Conditional("DEBUG")]
- public void Log(string status) => _logger.Log($"[{_stopWatch.Elapsed:ss\\.ff}] {status}");
- }
- }
-}
\ No newline at end of file
diff --git a/ModKit/ModKitSrc.projitems b/ModKit/ModKitSrc.projitems
deleted file mode 100644
index 4c984ea1d..000000000
--- a/ModKit/ModKitSrc.projitems
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
- $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- true
- 7d3f9428-c134-47fe-9d04-305b4d330401
- net472
- true
- LICENSE.txt
-
-
- ModKit
- 1.0.8
- A game agnostic toolkit for building Mod UI for unity mod manager based mods. Builds on ModMaker (https://github.com/cabarius/WrathModMaker and all past repos it was forked from)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ModKit/ModKitSrc.shproj b/ModKit/ModKitSrc.shproj
deleted file mode 100644
index a5f5fff0c..000000000
--- a/ModKit/ModKitSrc.shproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- 7d3f9428-c134-47fe-9d04-305b4d330401
- 14.0
-
-
-
-
-
-
-
-
diff --git a/ModKit/UI/Browser/BrowserModel.cs b/ModKit/UI/Browser/BrowserModel.cs
deleted file mode 100644
index bf60af29d..000000000
--- a/ModKit/UI/Browser/BrowserModel.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using ModKit.Utility;
-
-namespace ModKit {
- public class Entry {
- private Type[] Inheritance; // this may be able to become type
- private string Title;
- private string Subtitle;
- private string Description;
- private string Identifier;
- private SerializableDictionary extras;
- private string SearchKey;
- private string SortKey;
- private string[] Categories;
- private SerializableDictionary Tags; // indexed by category
- }
- public class Category {
- private string Name;
- public delegate bool CategoryChecker(T obj);
- public delegate string[] Tagger(T obj);
- public CategoryChecker IsCategory;
- public Tagger GetTags;
- }
-
- public abstract class DataSource {
- public delegate void Updater(List entries, int total, bool done = false);
- public delegate Entry DataTransformer(Data data);
- public Updater UpdateProgress;
- public DataTransformer Transformer;
- public bool IsLoading { get; private set; } = false;
- public bool IsLoaded { get; private set; } = false;
-
- private CancellationTokenSource _cancelToken;
-
- public void Start() {
- if (IsLoading) {
- _cancelToken.Cancel();
- IsLoading = false;
- }
- _cancelToken = new();
- IsLoading = true;
- Task.Run(() => LoadData());
- }
-
- public void Stop() {
- if (IsLoading) {
- IsLoading = false;
- _cancelToken.Cancel();
- }
- }
-
- protected abstract void LoadData();
-
- }
-
- interface IBrowserViewModel {
- public List Entries { get; }
- public HashSet Tags { get; }
- }
- public class BrowserModel- : IBrowserViewModel {
- public class Category
- : Category
{
- public delegate void OnGUI(Item item, Def def);
-
- public OnGUI OnHeaderGUI;
- public OnGUI OnRowGUI;
- public OnGUI OnDetailGUI;
- }
- public List> Categories;
- public List Entries { get; }
- public HashSet Tags { get; }
- }
-
- public class BrowserViewModel- : IBrowserViewModel {
- public BrowserModel
- .Category
- SelectedCategory { get; }
- public HashSet
SelectedTags;
- public string SearchText { get; }
- public List Entries { get; }
- public HashSet Tags { get; }
-
- }
-}
diff --git a/ModKit/UI/ColorUtils.cs b/ModKit/UI/ColorUtils.cs
deleted file mode 100644
index e0ba9e6a3..000000000
--- a/ModKit/UI/ColorUtils.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using UnityEngine;
-
-namespace ModKit {
- // https://docs.unity3d.com/Manual/StyledText.html
- public enum RGBA : uint {
- aqua = 0x00ffffff,
- blue = 0x8080ffff,
- brown = 0xC09050ff, //0xa52a2aff,
- crimson = 0x7b0340ff,
- cyan = 0x00ffffff,
- darkblue = 0x0000a0ff,
- charcoal = 0x202020ff,
- darkgrey = 0x808080ff,
- darkred = 0xa0333bff,
- fuchsia = 0xff40ffff,
- green = 0x40C040ff,
- gold = 0xED9B1Aff,
- lightblue = 0xd8e6ffff,
- lightgrey = 0xE8E8E8ff,
- lime = 0x40ff40ff,
- magenta = 0xff40ffff,
- maroon = 0xFF6060ff,
- medred = 0xd03333ff,
- navy = 0x3b5681ff,
- olive = 0xb0b000ff,
- orange = 0xffa500ff, // 0xffa500ff,
- darkorange = 0xb1521fff,
- pink = 0xf03399ff,
- purple = 0xC060F0ff,
- red = 0xFF4040ff,
- black = 0x000000ff,
- medgrey = 0xA8A8A8ff,
- grey = 0xC0C0C0ff,
- silver = 0xD0D0D0ff,
- teal = 0x80f0c0ff,
- yellow = 0xffff00ff,
- white = 0xffffffff,
- none = silver,
- trash = 0x808080ff, // 0x686868ff, 0x787878ff, // 0x734d26ff, // 0x86592dff, //0xA07040ff, // brown, 0x606060FF,
- common = 0xd8d8d8a0, // 0x505050ff, // 0xd8d8d8a0, //0xe8e8e8a0,
- uncommon = 0x086c26ff, // 0x00882bff, //0x00802bff, //0x68b020ff, // 0x60B020ff,
- rare = 0x103080ff, // 0x2060ffff,
- epic = 0x481ca0ff, //0x5020c0ff, 0x6030F0ff, 0xc260f1ff, // 0x79297bff, //0x9f608cff, // 0x885278ff, // 0xc260f1ff, //0xc860fff,
- legendary = 0xb2593aff, // 0xad5537ff, 0xb8603dff, 0xaa623dff, 0x9a5a3dff,0x9a4a2dff, (1.4.23 and older) 0xe67821e0, // 0x9a4a2dff, // 0x984c31ff, //0xe67821ff, //* 0xe67821e0, // 0xe67821ff, // 0xe68019ff // 0xEDCB1Aff,
- mythic = 0xb02369ff, //0xc02369ff, 0xf03399ff, 0xc260f1ff, 0x8080ffff, //0x60ffffff, // 0x84e2d4ff, // 0x2cd8d4ff, // * 0x60ffffff,
- primal = 0x2cb8b4ff, //pink, red
- godly = 0x990033ff, // darkred,
- notable = 0x98761fff, //0xb1821fff, 0xffe000ff, // 0xC08020ff //0xffd840ff, // 0x40ff40c0, // 0xf03399ff, // 0xff3399ff,
- uncommon_dark = 0x00a000ff, // 0x00882bff, //0x00802bff, //0x68b020ff, // 0x60B020ff,
- rare_dark = 0x1030e0ff, // 0x2060ffff,
- epic_dark = 0x6030F0ff, //0x5020c0ff, 0x6030F0ff, 0xc260f1ff, // 0x79297bff, //0x9f608cff, // 0x885278ff, // 0xc260f1ff, //0xc860fff,
- legendary_dark = 0xe67821e0, // 0xad5537ff, 0xb8603dff, 0xaa623dff, 0x9a5a3dff,0x9a4a2dff, (1.4.23 and older) 0xe67821e0, // 0x9a4a2dff, // 0x984c31ff, //0xe67821ff, //* 0xe67821e0, // 0xe67821ff, // 0xe68019ff // 0xEDCB1Aff,
- mythic_dark = 0xA000A0ff, //0xc02369ff, 0xf03399ff, 0xc260f1ff, 0x8080ffff, //0x60ffffff, // 0x84e2d4ff, // 0x2cd8d4ff, // * 0x60ffffff,
- primal_dark = 0x60ffffff, //pink, red
- godly_dark = 0xa00000ff, // darkred,
- notable_dark = 0x98761fff, //0xb1821fff, 0xffe000ff, // 0xC08020ff //0xffd840ff, // 0x40ff40c0, // 0xf03399ff, // 0xff3399ff,
- }
- public static class ColorUtils {
- public static Color color(this RGBA rga, float adjust = 0) {
- var red = (float)((long)rga >> 24) / 256f;
- var green = (float)(0xFF & ((long)rga >> 16)) / 256f;
- var blue = (float)(0xFF & ((long)rga >> 8)) / 256f;
- var alpha = (float)(0xFF & ((long)rga)) / 256f;
- var color = new Color(red, green, blue, alpha);
- if (adjust < 0)
- color = Color.Lerp(color, Color.black, -adjust);
- if (adjust > 0)
- color = Color.Lerp(color, Color.white, adjust);
- return color;
- }
- }
-}
diff --git a/ModKit/UI/GUIHelper.cs b/ModKit/UI/GUIHelper.cs
deleted file mode 100644
index 0d6f58de0..000000000
--- a/ModKit/UI/GUIHelper.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using UnityEngine;
-namespace ModKit.Utility {
- public static class GUIHelper {
- public const string onMark = $"{Glyphs.CheckOn()}! ";
- public const string offMark = $"{Glyphs.CheckOff()}! ";
-
- public static string FormatOn = Glyphs.DisclosureOn().color(RGBA.white).Bold() + " {0}";
- public static string FormatOff = Glyphs.DisclosureOff().color(RGBA.lime).Bold() + " {0}";
- public static string FormatNone = $" {Glyphs.DisclosureEmpty()}!".color(RGBA.white) + " {0}";
-
- public static string GetToggleText(ToggleState toggleState, string text) {
- return toggleState switch {
- ToggleState.Off => string.Format(FormatOff, text),
- ToggleState.On => string.Format(FormatOn, text),
- ToggleState.None => string.Format(FormatNone, text),
- _ => string.Format(FormatNone),
- };
- }
-
- public static int AdjusterButton(int value, string text, int min = int.MinValue, int max = int.MaxValue) {
- AdjusterButton(ref value, text, min, max);
- return value;
- }
-
- public static bool AdjusterButton(ref int value, string text, int min = int.MinValue, int max = int.MaxValue) {
- var oldValue = value;
- GUILayout.Label(text, GUILayout.ExpandWidth(false));
- if (GUILayout.Button("-", GUILayout.ExpandWidth(false)) && value > min)
- value--;
- GUILayout.Label(value.ToString(), GUILayout.ExpandWidth(false));
- if (GUILayout.Button("+", GUILayout.ExpandWidth(false)) && value < max)
- value++;
- return value != oldValue;
- }
-
- public static void Hyperlink(string url, Color normalColor, Color hoverColor, GUIStyle style) => Hyperlink(url, url, normalColor, hoverColor, style);
-
- public static void Hyperlink(string text, string url, Color normalColor, Color hoverColor, GUIStyle style) {
- var color = GUI.color;
- GUI.color = Color.clear;
- GUILayout.Label(text, style, GUILayout.ExpandWidth(false));
- var lastRect = GUILayoutUtility.GetLastRect();
- GUI.color = lastRect.Contains(Event.current.mousePosition) ? hoverColor : normalColor;
- if (GUI.Button(lastRect, text, style))
- Application.OpenURL(url);
- lastRect.y += lastRect.height - 2;
- lastRect.height = 1;
- GUI.DrawTexture(lastRect, Texture2D.whiteTexture, ScaleMode.StretchToFill);
- GUI.color = color;
- }
-
- public static void TextField(ref string value, GUIStyle style = null, params GUILayoutOption[] options) => value = GUILayout.TextField(value, style ?? GUI.skin.textField, options);
-
- public static void TextField(ref string value, Action onChanged, GUIStyle style = null, params GUILayoutOption[] options) => TextField(ref value, null, onChanged, style, options);
-
- public static void TextField(ref string value, Action onClear, Action onChanged, GUIStyle style = null, params GUILayoutOption[] options) {
- var old = value;
- TextField(ref value, style, options);
- if (value != old) {
- if (onClear != null && string.IsNullOrEmpty(value))
- onClear();
- else
- onChanged();
- }
- }
-#if false
- static bool CheckboxPrivate(
- ref bool value,
- string title,
- GUIStyle style = null,
- params GUILayoutOption[] options
- ) {
- bool changed = false;
- title = value ? title.Bold() : title.color(RGBA.lightgrey);
- if (UI.Toggle(title, ref value, 0, options)) changed = true;
- //if (GUILayout.Button("" + (value ? onMark : offMark) + " " + title, style, options)) { value = !value; }
- return changed;
- }
- public static bool Checkbox(
- ref bool value,
- String title,
- GUIStyle style = null,
- params GUILayoutOption[] options) {
- return CheckboxPrivate(ref value, title, style, options);
- }
-#endif
- public static ToggleState ToggleButton(ToggleState toggle, string text, GUIStyle style = null, params GUILayoutOption[] options) {
- UI.ToggleButton(ref toggle, text, style, options);
- return toggle;
- }
-
- public static ToggleState ToggleButton(ToggleState toggle, string text, Action on, Action off, GUIStyle style = null, params GUILayoutOption[] options) {
- ToggleButton(ref toggle, text, on, off, style, options);
- return toggle;
- }
-
- public static void ToggleButton(ref ToggleState toggle, string text, Action on, Action off, GUIStyle style = null, params GUILayoutOption[] options) {
- var old = toggle;
- UI.ToggleButton(ref toggle, text, style, options);
- if (toggle != old) {
- if (toggle.IsOn())
- on?.Invoke();
- else
- off?.Invoke();
- }
- }
-
- public static void ToggleButton(ref ToggleState toggle, string text, ref float minWidth, GUIStyle style = null, params GUILayoutOption[] options) {
- GUIContent content = new(GetToggleText(toggle, text));
- style ??= GUI.skin.button;
- minWidth = Math.Max(minWidth, style.CalcSize(content).x);
- if (GUILayout.Button(content, style, options?.Concat(new[] { GUILayout.Width(minWidth) }).ToArray() ?? new[] { GUILayout.Width(minWidth) }))
- toggle = toggle.Flip();
- }
-
- public static void ToggleButton(ref ToggleState toggle, string text, ref float minWidth, Action on, Action off, GUIStyle style = null, params GUILayoutOption[] options) {
- var old = toggle;
- ToggleButton(ref toggle, text, ref minWidth, style, options);
- if (toggle != old) {
- if (toggle.IsOn())
- on?.Invoke();
- else
- off?.Invoke();
- }
- }
-
- public static ToggleState ToggleTypeList(ToggleState toggle, string text, HashSet selectedTypes, HashSet allTypes, GUIStyle style = null, params GUILayoutOption[] options) {
- GUILayout.BeginHorizontal();
-
- UI.ToggleButton(ref toggle, text, style, options);
-
- if (toggle.IsOn()) {
- using (new GUILayout.VerticalScope()) {
- using (new GUILayout.HorizontalScope()) {
- if (GUILayout.Button("Select All")) {
- foreach (var type in allTypes) {
- selectedTypes.Add(type.FullName);
- }
- }
- if (GUILayout.Button("Deselect All")) {
- selectedTypes.Clear();
- }
- }
-
- foreach (var type in allTypes) {
- ToggleButton(selectedTypes.Contains(type.FullName) ? ToggleState.On : ToggleState.Off, type.Name.ToSentence(),
- () => selectedTypes.Add(type.FullName),
- () => selectedTypes.Remove(type.FullName),
- style, options);
- }
- }
- }
-
- GUILayout.EndHorizontal();
-
- return toggle;
- }
-
- public static void Toolbar(ref int selected, string[] texts, GUIStyle style = null, params GUILayoutOption[] options) => selected = GUILayout.Toolbar(selected, texts, style ?? GUI.skin.button, options);
-
- public static void SelectionGrid(ref int selected, string[] texts, int xCount, GUIStyle style = null, params GUILayoutOption[] options) => selected = GUILayout.SelectionGrid(selected, texts, xCount, style ?? GUI.skin.button, options);
-
- public static void SelectionGrid(ref int selected, string[] texts, int xCount, Action onChanged, GUIStyle style = null, params GUILayoutOption[] options) {
- var old = selected;
- SelectionGrid(ref selected, texts, xCount, style, options);
- if (selected != old) {
- onChanged?.Invoke();
- }
- }
-
- public static float RoundedHorizontalSlider(float value, int digits, float leftValue, float rightValue, params GUILayoutOption[] options) {
- if (digits < 0) {
- var num = (float)Math.Pow(10d, -digits);
- return (float)Math.Round(GUILayout.HorizontalSlider(value, leftValue, rightValue, options) / num, 0) * num;
- }
- else {
- return (float)Math.Round(GUILayout.HorizontalSlider(value, leftValue, rightValue, options), digits);
- }
- }
-
- private static Texture2D fillTexture = null;
- private static GUIStyle fillStyle = null;
- private static Color fillColor = new(1f, 1f, 1f, 0.65f);
- private static readonly Color color = new(1f, 1f, 1f, 0.35f);
- private static Color fillColor2 = color;
-
- public static Color FillColor2 { get => fillColor2; set => fillColor2 = value; }
-
- public static GUIStyle FillStyle(Color color) {
- if (fillTexture == null)
- fillTexture = new Texture2D(1, 1);
- if (fillStyle == null)
- fillStyle = new GUIStyle();
- fillTexture.SetPixel(0, 0, color);
- fillTexture.Apply();
- fillStyle.normal.background = fillTexture;
- return fillStyle;
- }
- public static void GUIDrawRect(Rect position, Color color) => GUI.Box(position, GUIContent.none, FillStyle(color));
- //private static GUIStyle divStyle;
- public static void Div(Color color, float indent = 0, float height = 0, float width = 0) {
- if (fillTexture == null)
- fillTexture = new Texture2D(1, 1);
- var divStyle = new GUIStyle {
- fixedHeight = 1
- };
- fillTexture.SetPixel(0, 0, color);
- fillTexture.Apply();
- divStyle.normal.background = fillTexture;
- divStyle.margin = new RectOffset((int)indent, 0, 4, 4);
- if (width > 0)
- divStyle.fixedWidth = width;
- else
- divStyle.fixedWidth = 0;
- GUILayout.Space((1f * height) / 2f);
- GUILayout.Box(GUIContent.none, divStyle);
- GUILayout.Space(height / 2f);
- }
-
- public static void Div(float indent = 0, float height = 25, float width = 0) => Div(fillColor, indent, height, width);
-
- }
-}
diff --git a/ModKit/UI/Private/Toggle.cs b/ModKit/UI/Private/Toggle.cs
deleted file mode 100644
index 288aa51b9..000000000
--- a/ModKit/UI/Private/Toggle.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-using UnityEngine;
-
-namespace ModKit.Private {
- public static partial class UI {
-
- // Helper functionality.
-
- private static readonly GUIContent _LabelContent = new();
- public static readonly GUIContent CheckOn = new(ModKit.UI.ChecklyphOn);
- public static readonly GUIContent CheckOff = new(ModKit.UI.CheckGlyphOff);
- public static readonly GUIContent DisclosureOn = new(ModKit.UI.DisclosureGlyphOn);
- public static readonly GUIContent DisclosureOff = new(ModKit.UI.DisclosureGlyphOff);
- public static readonly GUIContent DisclosureEmpty = new(ModKit.UI.DisclosureGlyphEmpty);
- private static GUIContent LabelContent(string text) {
- _LabelContent.text = text;
- _LabelContent.image = null;
- _LabelContent.tooltip = null;
- return _LabelContent;
- }
-
- private static readonly int s_ButtonHint = "MyGUI.Button".GetHashCode();
-
- public static bool Toggle(Rect rect, GUIContent label, bool value, bool isEmpty, GUIContent on, GUIContent off, GUIStyle stateStyle, GUIStyle labelStyle) {
- var controlID = GUIUtility.GetControlID(s_ButtonHint, FocusType.Passive, rect);
- var result = false;
- switch (Event.current.GetTypeForControl(controlID)) {
- case EventType.MouseDown:
- if (GUI.enabled && rect.Contains(Event.current.mousePosition)) {
- GUIUtility.hotControl = controlID;
- Event.current.Use();
- }
- break;
-
- case EventType.MouseDrag:
- if (GUIUtility.hotControl == controlID) {
- Event.current.Use();
- }
- break;
-
- case EventType.MouseUp:
- if (GUIUtility.hotControl == controlID) {
- GUIUtility.hotControl = 0;
-
- if (rect.Contains(Event.current.mousePosition)) {
- result = true;
- Event.current.Use();
- }
- }
- break;
-
- case EventType.KeyDown:
- if (GUIUtility.hotControl == controlID) {
- if (Event.current.keyCode == KeyCode.Escape) {
- GUIUtility.hotControl = 0;
- Event.current.Use();
- }
- }
- break;
-
- case EventType.Repaint: {
- //bool leftAlign = stateStyle.alignment == TextAnchor.MiddleLeft
- // || stateStyle.alignment == TextAnchor.UpperLeft
- // || stateStyle.alignment == TextAnchor.LowerLeft
- // ;
- var rightAlign = stateStyle.alignment == TextAnchor.MiddleRight
- || stateStyle.alignment == TextAnchor.UpperRight
- || stateStyle.alignment == TextAnchor.LowerRight
- ;
- // stateStyle.alignment determines position of state element
- var state = isEmpty ? DisclosureEmpty : value ? on : off;
- var stateSize = stateStyle.CalcSize(value ? on : off); // don't use the empty content to calculate size so titles line up in lists
- var x = rightAlign ? rect.xMax - stateSize.x : rect.x;
- Rect stateRect = new(x, rect.y, stateSize.x, stateSize.y);
-
- // layout state before or after following alignment
- var labelSize = labelStyle.CalcSize(label);
- x = rightAlign ? stateRect.x - stateSize.x - 5 : stateRect.xMax + 5;
- Rect labelRect = new(x, rect.y, labelSize.x, labelSize.y);
-
- stateStyle.Draw(stateRect, state, controlID);
- labelStyle.Draw(labelRect, label, controlID);
- }
- break;
- }
- return result;
- }
-
- // Button Control - Layout Version
-
-#if false
- static Vector2 cachedArrowSize = new Vector2(0, 0);
- public static bool Toggle(GUIContent label, bool value, GUIContent on, GUIContent off, GUIStyle stateStyle, GUIStyle labelStyle, params GUILayoutOption[] options) {
- var style = new GUIStyle(labelStyle);
- if (cachedArrowSize.x == 0)
- cachedArrowSize = style.CalcSize(off);
- RectOffset padding = new RectOffset(0, (int)cachedArrowSize.x + 10, 0, 0);
- style.padding = padding;
- Rect rect = GUILayoutUtility.GetRect(label, style, options);
- return Toggle(rect, label, value, on, off, stateStyle, style);
- }
-#else
- public static bool Toggle(GUIContent label, bool value, GUIContent on, GUIContent off, GUIStyle stateStyle, GUIStyle labelStyle, bool isEmpty = false, params GUILayoutOption[] options) {
- var state = value ? on : off;
- var sStyle = new GUIStyle(stateStyle);
- var lStyle = new GUIStyle(labelStyle) {
- wordWrap = false
- };
- var stateSize = sStyle.CalcSize(state);
- lStyle.fixedHeight = stateSize.y - 2;
- var padding = new RectOffset(0, (int)stateSize.x + 5, 0, 0);
- lStyle.padding = padding;
- var rect = GUILayoutUtility.GetRect(label, lStyle, options);
-#if false
- var labelSize = lStyle.CalcSize(label);
- var width = stateSize.x + 10 + stateSize.x;
- var height = Mathf.Max(stateSize.y, labelSize.y);
- var rect = GUILayoutUtility.GetRect(width, height);
- int controlID = GUIUtility.GetControlID(s_ButtonHint, FocusType.Passive, rect);
- var eventType = Event.current.GetTypeForControl(controlID);
-
- Logger.Log($"event: {eventType.ToString()} label: {label.text} w: {width} h: {height} rect: {rect} options: {options.Length}");
-#endif
- return Toggle(rect, label, value, isEmpty, on, off, stateStyle, labelStyle);
- }
-#endif
- public static bool Toggle(string label, bool value, string on, string off, GUIStyle stateStyle, GUIStyle labelStyle, params GUILayoutOption[] options) => Toggle(LabelContent(label), value, new GUIContent(on), new GUIContent(off), stateStyle, labelStyle, false, options);
- // Disclosure Toggles
- public static bool DisclosureToggle(GUIContent label, bool value, bool isEmpty = false, params GUILayoutOption[] options) => Toggle(label, value, DisclosureOn, DisclosureOff, GUI.skin.textArea, GUI.skin.label, isEmpty, options);
- public static bool DisclosureToggle(string label, bool value, GUIStyle stateStyle, GUIStyle labelStyle, bool isEmpty = false, params GUILayoutOption[] options) => Toggle(LabelContent(label), value, DisclosureOn, DisclosureOff, stateStyle, labelStyle, isEmpty, options);
- public static bool DisclosureToggle(string label, bool value, bool isEmpty = false, params GUILayoutOption[] options) => DisclosureToggle(label, value, GUI.skin.box, GUI.skin.label, isEmpty, options);
- // CheckBox
- public static bool CheckBox(GUIContent label, bool value, bool isEmpty, params GUILayoutOption[] options) => Toggle(label, value, CheckOn, CheckOff, GUI.skin.textArea, GUI.skin.label, isEmpty, options);
-
- public static bool CheckBox(string label, bool value, bool isEmpty, GUIStyle style, params GUILayoutOption[] options) => Toggle(LabelContent(label), value, CheckOn, CheckOff, GUI.skin.box, style, isEmpty, options);
-
- public static bool CheckBox(string label, bool value, bool isEmpty, params GUILayoutOption[] options) => CheckBox(label, value, isEmpty, GUI.skin.label, options);
- }
-}
\ No newline at end of file
diff --git a/ModKit/UI/RichText.cs b/ModKit/UI/RichText.cs
deleted file mode 100644
index 0aea56185..000000000
--- a/ModKit/UI/RichText.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// borrowed shamelessly and enhanced from Bag of Tricks https://www.nexusmods.com/pathfinderkingmaker/mods/26, which is under the MIT License
-using System.Linq;
-using System.Text.RegularExpressions;
-using UnityEngine;
-
-namespace ModKit {
- public static class RichText {
- public static string ToHtmlString(this RGBA color) => $"{color:X}";
- public static string ToAcronym(this string s) => string.Concat(s.Where((c, i) => char.IsUpper(c) || (char.IsLetter(c) && i == 0)));
- public static string size(this string s, int size) => _ = $"{s} ";
- public static string mainCategory(this string s) => s.size(16).bold();
-
- public static string bold(this string s) => _ = $"{s} ";
- public static string italic(this string s) => _ = $"{s} ";
- public static string color(this string s, string color) => _ = $"{s} ";
- public static string color(this string str, RGBA color) => $"{str} ";
- public static string color(this string str, Color32 color) => $"{str} ";
- public static string color(this string str, Color color) => $"{str} ";
- public static string colorCaps(this string str, RGBA color) => Regex.Replace(str, @"([A-Z])([A-Za-z]+)",
- "$1".color(color) + "$2");
- public static string white(this string s) => s.color("white");
-
- public static string grey(this string s) => s.color("#A0A0A0FF");
- public static string darkGrey(this string s) => s.color("#505050FF");
-
- public static string red(this string s) => s.color("#C04040E0");
-
- public static string pink(this string s) => s.color("#FFA0A0E0");
-
- public static string green(this string s) => s.color("#00ff00ff");
-
- public static string blue(this string s) => s.color("blue");
-
- public static string cyan(this string s) => s.color("cyan");
-
- public static string magenta(this string s) => s.color("magenta");
-
- public static string yellow(this string s) => s.color("yellow");
-
- public static string orange(this string s) => s.color("orange");
-
- public static string warningLargeRedFormat(this string s) => _ = s.red().size(16).bold();
-
- public static string sizePercent(this string s, int percent) => _ = $"{s} ";
- }
-}
-
diff --git a/ModKit/UI/UI+Browser.cs b/ModKit/UI/UI+Browser.cs
deleted file mode 100644
index 37b637dc7..000000000
--- a/ModKit/UI/UI+Browser.cs
+++ /dev/null
@@ -1,355 +0,0 @@
-using JetBrains.Annotations;
-using ModKit.Utility;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using UnityEngine;
-
-namespace ModKit {
-
- public static partial class UI {
- public class Browser {
-
- private static readonly Dictionary ShowDetails = new();
- public static void ClearDetails() => ShowDetails.Clear();
- public static bool DetailToggle(string title, object key, object target = null, int width = 600) {
- var changed = false;
- if (target == null) target = key;
- var expanded = ShowDetails.ContainsKey(key);
- if (DisclosureToggle(title, ref expanded, width)) {
- changed = true;
- ShowDetails.Clear();
- if (expanded) {
- ShowDetails[key] = target;
- }
- }
- return changed;
- }
- public static bool OnDetailGUI(object key, Action onDetailGUI) {
-
- ShowDetails.TryGetValue(key, out var target);
- if (target != null) {
- onDetailGUI(target);
- return true;
- }
- else {
- return false;
- }
- }
- }
- public class Browser : Browser {
- // Simple browser that displays a searchable collection of items along with a collection of available definitions.
- // It provides a toggle to show the definitions mixed in with the items.
- // By default it shows just the title but you can provide an optional RowUI to show more complex row UI including buttons to add and remove items and so forth. This will be layed out after the title
- // You an also provide UI that renders children below the row
- public ModKitSettings Settings => Mod.ModKitSettings;
- private IEnumerable _pagedResults = new List();
- private Queue cachedSearchResults;
- public List filteredDefinitions;
- private Dictionary _currentDict;
-
- private CancellationTokenSource _cancellationTokenSource;
- private string _searchText = "";
- public string SearchText => _searchText;
- public bool SearchAsYouType;
- public bool ShowAll;
- public bool IsDetailBrowser;
-
- public int SearchLimit {
- get => IsDetailBrowser ? Settings.browserDetailSearchLimit : Settings.browserSearchLimit;
- set {
- var oldValue = SearchLimit;
- if (IsDetailBrowser)
- Settings.browserDetailSearchLimit = value;
- else
- Settings.browserSearchLimit = value;
- if (value != oldValue) ModKitSettings.Save();
- }
- }
- private int _pageCount;
- private int _matchCount;
- private int _currentPage = 1;
- private bool _searchQueryChanged = true;
- public void ResetSearch() {
- _searchQueryChanged = true;
- ReloadData();
- }
- public bool needsReloadData = true;
- public void ReloadData() => needsReloadData = true;
- private bool _updatePages = false;
- private bool _finishedSearch = false;
- public bool isSearching = false;
- public bool startedLoadingAvailable = false;
- public bool availableIsStatic { get; private set; }
- private List _availableCache;
- public void OnShowGUI() => needsReloadData = true;
- public Browser(bool searchAsYouType = true, bool availableIsStatic = false, bool isDetailBrowser = false) {
- SearchAsYouType = searchAsYouType;
- this.availableIsStatic = availableIsStatic;
- IsDetailBrowser = isDetailBrowser;
- Mod.NotifyOnShowGUI += OnShowGUI;
- }
-
- public void OnGUI(
- IEnumerable- current,
- Func
> available, // Func because available may be slow
- Func- definition,
- Func
searchKey,
- Func sortKey,
- Action onHeaderGUI = null,
- Action onRowGUI = null,
- Action onDetailGUI = null,
- int indent = 50,
- bool showDiv = true,
- bool search = true,
- float titleMinWidth = 100,
- float titleMaxWidth = 300,
- string searchTextPassedFromParent = "",
- bool showItemDiv = false
- ) {
- current ??= new List- ();
- List
definitions = Update(current, available, search, searchKey, sortKey, definition);
- if (search || SearchLimit < _matchCount) {
- if (search) {
- using (HorizontalScope()) {
- indent.space();
- ActionTextField(ref _searchText, "searchText", (text) => {
- if (!SearchAsYouType) return;
- needsReloadData = true;
- _searchQueryChanged = true;
- }, () => { needsReloadData = true; }, MinWidth(320), AutoWidth());
- 25.space();
- Label("Limit", ExpandWidth(false));
- var searchLimit = SearchLimit;
- ActionIntTextField(ref searchLimit, "Search Limit", (i) => { _updatePages = true; }, () => { _updatePages = true; }, width(175));
- if (searchLimit > 1000) { searchLimit = 1000; }
- SearchLimit = searchLimit;
- 25.space();
- if (DisclosureToggle("Show All".Orange().Bold(), ref ShowAll)) {
- startedLoadingAvailable |= ShowAll;
- ResetSearch();
- }
- 25.space();
- // if (isSearching && false) { // ADDB - Please add a delay timer before this appears because having it flash on very short searches is distracting or let's just get rid of it
- // Label("Searching...", AutoWidth());
- // 25.space();
- // }
- }
- }
- else {
- if (_searchText != searchTextPassedFromParent) {
- needsReloadData = true;
- _searchText = searchTextPassedFromParent;
- if (_searchText == null) {
- _searchText = "";
- }
- }
- }
- using (HorizontalScope()) {
- if (search) {
- space(indent);
- ActionButton("Search", () => { needsReloadData = true; }, AutoWidth());
- }
- space(25);
- if (_matchCount > 0 || _searchText.Length > 0) {
- var matchesText = "Matches: ".Green().Bold() + $"{_matchCount}".Orange().Bold();
- if (_matchCount > SearchLimit) { matchesText += " => ".Cyan() + $"{SearchLimit}".Cyan().Bold(); }
-
- Label(matchesText, ExpandWidth(false));
- }
- if (_matchCount > SearchLimit) {
- string pageLabel = "Page: ".orange() + _currentPage.ToString().cyan() + " / " + _pageCount.ToString().cyan();
- 25.space();
- Label(pageLabel, ExpandWidth(false));
- ActionButton("-", () => {
- if (_currentPage >= 1) {
- if (_currentPage == 1) {
- _currentPage = _pageCount;
- }
- else {
- _currentPage -= 1;
- }
- _updatePages = true;
- }
- }, AutoWidth());
- ActionButton("+", () => {
- if (_currentPage > _pageCount) return;
- if (_currentPage == _pageCount) {
- _currentPage = 1;
- }
- else {
- _currentPage += 1;
- }
- _updatePages = true;
- }, AutoWidth());
- }
- }
- }
- if (showDiv)
- Div(indent);
- if (onHeaderGUI != null) {
- using (HorizontalScope(AutoWidth())) {
- space(indent);
- onHeaderGUI();
- }
- }
- foreach (var def in definitions) {
- if (showItemDiv) {
- Div(indent);
- }
- _currentDict.TryGetValue(def, out var item);
- if (onRowGUI != null) {
- using (HorizontalScope(AutoWidth())) {
- space(indent);
- onRowGUI(def, item);
- }
- }
- onDetailGUI?.Invoke(def, item);
- }
- }
-
- private List Update(IEnumerable- current, Func
> available, bool search,
- Func searchKey, Func sortKey, Func- definition) {
- if (Event.current.type == EventType.Layout) {
- if (startedLoadingAvailable) {
- _availableCache = available()?.ToList();
- if (_availableCache?.Count() > 0) {
- startedLoadingAvailable = false;
- needsReloadData = true;
- if (!availableIsStatic) {
- _availableCache = null;
- }
- }
- }
- if (_finishedSearch || isSearching) {
- bool nothingToSearch = (!ShowAll && current.Count() == 0) || (ShowAll && (availableIsStatic ? _availableCache : available()).Count() == 0);
- // If the search has at least one result
- if ((cachedSearchResults.Count > 0 || nothingToSearch) && (_searchQueryChanged || _finishedSearch)) {
- if (_finishedSearch && !_searchQueryChanged) {
- filteredDefinitions = new List
();
- }
- // Lock the search results
- lock (cachedSearchResults) {
- // Go through every item in the queue
- while (cachedSearchResults.Count > 0) {
- // Add the item into the OrderedSet filteredDefinitions
- filteredDefinitions.Add(cachedSearchResults.Dequeue());
- }
- }
- filteredDefinitions.Sort(Comparer.Create((x, y) => sortKey(x).CompareTo(sortKey(y))));
- }
- _matchCount = filteredDefinitions.Count;
- UpdatePageCount();
- UpdatePaginatedResults();
- if (_finishedSearch) {
- isSearching = false;
- _updatePages = false;
- _finishedSearch = false;
- _searchQueryChanged = false;
- cachedSearchResults = null;
- }
- }
- if (needsReloadData) {
- _currentDict = current.ToDictionaryIgnoringDuplicates(definition, c => c);
- IEnumerable definitions;
- if (ShowAll) {
- if (startedLoadingAvailable) {
- definitions = _currentDict.Keys.ToList();
- }
- else if (availableIsStatic) {
- definitions = _availableCache;
- }
- else {
- definitions = available();
- }
- }
- else {
- definitions = _currentDict.Keys.ToList();
- }
- if (!isSearching) {
- _cancellationTokenSource = new();
- Task.Run(() => UpdateSearchResults(_searchText, definitions, searchKey, sortKey, search));
- if (_searchQueryChanged) {
- filteredDefinitions = new List();
- }
- isSearching = true;
- needsReloadData = false;
- }
- else {
- _cancellationTokenSource.Cancel();
- }
- }
- if (_updatePages) {
- _updatePages = false;
- UpdatePageCount();
- UpdatePaginatedResults();
- }
- }
- return _pagedResults?.ToList();
- }
-
- [UsedImplicitly]
- public void UpdateSearchResults(string searchTextParam,
- IEnumerable definitions,
- Func searchKey,
- Func sortKey,
- bool search
- ) {
- if (definitions == null) {
- return;
- }
- cachedSearchResults = new();
- var terms = searchTextParam.Split(' ').Select(s => s.ToLower()).ToHashSet();
- if (search) {
- foreach (var def in definitions) {
- if (_cancellationTokenSource.IsCancellationRequested) {
- isSearching = false;
- return;
- }
- if (def.GetType().ToString().Contains(searchTextParam)
- ) {
- lock (cachedSearchResults) {
- cachedSearchResults.Enqueue(def);
- }
- }
- else if (searchKey != null) {
- var text = searchKey(def).ToLower();
- if (terms.All(term => text.Matches(term))) {
- lock (cachedSearchResults) {
- cachedSearchResults.Enqueue(def);
- }
- }
- }
- }
- }
- else {
- lock (cachedSearchResults) {
- cachedSearchResults = new Queue(definitions);
- }
- }
- _finishedSearch = true;
- }
- public void UpdatePageCount() {
- if (SearchLimit > 0) {
- _pageCount = (int)Math.Ceiling((double)_matchCount / SearchLimit);
- _currentPage = Math.Min(_currentPage, _pageCount);
- _currentPage = Math.Max(1, _currentPage);
- }
- else {
- _pageCount = 1;
- _currentPage = 1;
- }
- }
- public void UpdatePaginatedResults() {
- var limit = SearchLimit;
- var count = _matchCount;
- var offset = Math.Min(count, (_currentPage - 1) * limit);
- limit = Math.Min(limit, Math.Max(count, count - limit));
- Mod.Trace($"{_currentPage} / {_pageCount} count: {count} => offset: {offset} limit: {limit} ");
- _pagedResults = filteredDefinitions.Skip(offset).Take(limit).ToArray();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/ModKit/Utility/Dictionary/DoubleDictionary.cs b/ModKit/Utility/Dictionary/DoubleDictionary.cs
deleted file mode 100644
index eacffe0ef..000000000
--- a/ModKit/Utility/Dictionary/DoubleDictionary.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace ModKit.Utility {
- public class DoubleDictionary {
- private readonly Dictionary> _dictionary
- = new();
-
- public TValue this[TKey1 key1, TKey2 key2] {
- get {
- return _dictionary[key1][key2];
- }
- set {
- _dictionary[key1][key2] = value;
- }
- }
-
- public void Add(TKey1 key1, TKey2 key2, TValue value) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary)) {
- _dictionary.Add(key1, innerDictionary = new Dictionary());
- }
- innerDictionary.Add(key2, value);
- }
-
- public void Clear() => _dictionary.Clear();
-
- public bool TryGetValue(TKey1 key1, TKey2 key2, out TValue value) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary)) {
- lock (_dictionary) {
- if (!_dictionary.TryGetValue(key1, out innerDictionary)) {
- _dictionary.Add(key1, innerDictionary = new Dictionary());
- }
- }
- }
- return innerDictionary.TryGetValue(key2, out value);
- }
-
- public TValue GetValueOrDefault(TKey1 key1, TKey2 key2, Func getDefault) {
- lock (_dictionary) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary)) {
- _dictionary.Add(key1, innerDictionary = new Dictionary());
- }
- if (!innerDictionary.TryGetValue(key2, out var value)) {
- innerDictionary.Add(key2, value = getDefault());
- }
- return value;
- }
- }
- }
-}
diff --git a/ModKit/Utility/Dictionary/TripleDictionary.cs b/ModKit/Utility/Dictionary/TripleDictionary.cs
deleted file mode 100644
index dad6665d0..000000000
--- a/ModKit/Utility/Dictionary/TripleDictionary.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace ModKit.Utility {
- public class TripleDictionary {
- private readonly Dictionary>> _dictionary
- = new();
-
- public TValue this[TKey1 key1, TKey2 key2, TKey3 key3] {
- get {
- return _dictionary[key1][key2][key3];
- }
- set {
- _dictionary[key1][key2][key3] = value;
- }
- }
-
- public void Add(TKey1 key1, TKey2 key2, TKey3 key3, TValue value) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary1)) {
- _dictionary.Add(key1, innerDictionary1 = new Dictionary>());
- }
- if (!innerDictionary1.TryGetValue(key2, out var innerDictionary2)) {
- innerDictionary1.Add(key2, innerDictionary2 = new Dictionary());
- }
- innerDictionary2.Add(key3, value);
- }
-
- public void Clear() => _dictionary.Clear();
-
- public bool TryGetValue(TKey1 key1, TKey2 key2, TKey3 key3, out TValue value) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary1)) {
- _dictionary.Add(key1, innerDictionary1 = new Dictionary>());
- }
- if (!innerDictionary1.TryGetValue(key2, out var innerDictionary2)) {
- innerDictionary1.Add(key2, innerDictionary2 = new Dictionary());
- }
- return innerDictionary2.TryGetValue(key3, out value);
- }
-
- public TValue GetValueOrDefault(TKey1 key1, TKey2 key2, TKey3 key3, Func getDefault) {
- if (!_dictionary.TryGetValue(key1, out var innerDictionary1)) {
- _dictionary.Add(key1, innerDictionary1 = new Dictionary>());
- }
- if (!innerDictionary1.TryGetValue(key2, out var innerDictionary2)) {
- innerDictionary1.Add(key2, innerDictionary2 = new Dictionary());
- }
- if (!innerDictionary2.TryGetValue(key3, out var value)) {
- innerDictionary2.Add(key3, value = getDefault());
- }
- return value;
- }
- }
-}
diff --git a/ModKit/Utility/EmbeddedResourceUtils.cs b/ModKit/Utility/EmbeddedResourceUtils.cs
deleted file mode 100644
index ca07f03d3..000000000
--- a/ModKit/Utility/EmbeddedResourceUtils.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-
-namespace ModKit {
- public static class EmbeddedResourceUtils {
- public static Stream StreamForResourceFile(string endingFileName) {
- var assembly = Assembly.GetExecutingAssembly();
- var manifestResourceNames = assembly.GetManifestResourceNames();
-
- foreach (var resourceName in manifestResourceNames) {
- var fileNameFromResourceName = _GetFileNameFromResourceName(resourceName);
- if (!fileNameFromResourceName.EndsWith(endingFileName)) {
- continue;
- }
-
- using var manifestResourceStream = assembly.GetManifestResourceStream(resourceName);
- if (manifestResourceStream == null) {
- continue;
- }
- return manifestResourceStream;
- }
- return null;
- }
-
- // https://stackoverflow.com/a/32176198/3764804
- private static string _GetFileNameFromResourceName(string resourceName) {
- var stringBuilder = new StringBuilder();
- var escapeDot = false;
- var haveExtension = false;
-
- for (var resourceNameIndex = resourceName.Length - 1;
- resourceNameIndex >= 0;
- resourceNameIndex--) {
- if (resourceName[resourceNameIndex] == '_') {
- escapeDot = true;
- continue;
- }
-
- if (resourceName[resourceNameIndex] == '.') {
- if (!escapeDot) {
- if (haveExtension) {
- stringBuilder.Append('\\');
- continue;
- }
-
- haveExtension = true;
- }
- }
- else {
- escapeDot = false;
- }
-
- stringBuilder.Append(resourceName[resourceNameIndex]);
- }
-
- var fileName = Path.GetDirectoryName(stringBuilder.ToString());
- return fileName == null ? null : new string(fileName.Reverse().ToArray());
- }
- }
-}
\ No newline at end of file
diff --git a/ModKit/Utility/Extensions/RichTextExtensions.cs b/ModKit/Utility/Extensions/RichTextExtensions.cs
deleted file mode 100644
index 08dd693ec..000000000
--- a/ModKit/Utility/Extensions/RichTextExtensions.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-//#define MARK_DEBUG
-using System;
-using System.Text;
-using System.Text.RegularExpressions;
-using UnityEngine;
-
-namespace ModKit.Utility {
- public static class StringExtensions {
- public static bool Matches(this string source, string query) {
- if (source == null || query == null)
- return false;
-#if false
- return source.IndexOf(other, 0, StringComparison.InvariantCulture) != -1;
-#else
- return source.IndexOf(query, 0, StringComparison.InvariantCultureIgnoreCase) != -1;
-#endif
- }
- public static bool Matches(this string source, string[] queryTerms) {
- var matchCount = 0;
- foreach (var term in queryTerms) {
- if (source.IndexOf(term, 0, StringComparison.InvariantCultureIgnoreCase) != -1)
- matchCount += 1;
- }
- return matchCount >= queryTerms.Length;
- }
- public static string MarkedSubstringNoHTML(this string source, string sub) {
- if (string.IsNullOrWhiteSpace(source) || string.IsNullOrWhiteSpace(sub))
- return source;
- var index = source.IndexOf(sub, StringComparison.InvariantCultureIgnoreCase);
- if (index != -1) {
- var substr = source.Substring(index, sub.Length);
- source = source.Replace(substr, substr.yellow().Bold());
- }
- return source;
- }
- public static string MarkedSubstring(this string source, string[] queryTerms) {
- foreach (var term in queryTerms) {
- source = source.MarkedSubstring(term);
- }
- return source;
- }
- public static string MarkedSubstring(this string source, string query) {
- if (string.IsNullOrWhiteSpace(source) || string.IsNullOrWhiteSpace(query))
- return source;
- var htmlStart = source.IndexOf('<');
- if (htmlStart == -1)
- return source.MarkedSubstringNoHTML(query);
- var result = new StringBuilder();
- var len = source.Length;
- var segment = source.Substring(0, htmlStart);
- #if MARK_DEBUG
- bool detail = source.Contains("More");
- if (detail) Mod.Debug($"{query} in {source}");
- #endif
- var cnt = 0;
- segment = segment.MarkedSubstringNoHTML(query);
- #if MARK_DEBUG
- if (detail) Mod.Log($"{(cnt++)} - segment - (0, {htmlStart}) {segment} ");
- #endif
- result.Append(segment);
- var htmlEnd = source.IndexOf('>', htmlStart);
- while (htmlStart != -1 && htmlEnd != -1) {
- var tag = source.Substring(htmlStart, htmlEnd + 1 - htmlStart);
- #if MARK_DEBUG
- if (detail) Mod.Log($"{(cnt++)} - tag - ({htmlStart}, {htmlEnd}) {tag} ");
- #endif
- result.Append(tag);
- htmlStart = source.IndexOf('<', htmlEnd);
- if (htmlStart != -1) {
- segment = source.Substring(htmlEnd + 1, htmlStart - htmlEnd - 1);
- segment = segment.MarkedSubstringNoHTML(query);
- #if MARK_DEBUG
- if (detail) Mod.Log($"{(cnt++)} - segment - ({htmlEnd+1}, {htmlStart}) {segment} ");
- #endif
- result.Append(segment);
- htmlEnd = source.IndexOf('>', htmlStart);
- }
- }
- if (htmlStart != -1) {
- var malformedTag = source.Substring(htmlStart, len + 1 - htmlStart);
- result.Append(malformedTag);
- #if MARK_DEBUG
- if (detail) Mod.Log($"{(cnt++)} - badtag - ({htmlEnd + 1}, {htmlStart}) {malformedTag} ");
- #endif
- }
- else if (htmlEnd < len) {
- segment = source.Substring(htmlEnd + 1, len - htmlEnd - 1);
- #if MARK_DEBUG
- if (detail) Mod.Log($"{(cnt++)} - segment - ({htmlEnd + 1}, {len}) {segment} ");
- #endif
- result.Append(segment.MarkedSubstringNoHTML(query));
- }
- return result.ToString();
- }
- public static string Repeat(this string s, int n) {
- if (n < 0 || s == null || s.Length == 0)
- return s;
- return new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
- }
- public static string Indent(this string s, int n) => " ".Repeat(n) + s;
-
- }
-
- public static class RichTextExtensions {
- // https://docs.unity3d.com/Manual/StyledText.html
-
- public enum RGBA : uint {
- aqua = 0x00ffffff,
- blue = 0x8080ffff,
- brown = 0xC09050ff, //0xa52a2aff,
- cyan = 0x00ffffff,
- darkblue = 0x0000a0ff,
- fuchsia = 0xff40ffff,
- green = 0x40C040ff,
- lightblue = 0xd8e6ff,
- lime = 0x40ff40ff,
- magenta = 0xff40ffff,
- maroon = 0xFF6060ff,
- navy = 0x000080ff,
- olive = 0xB0B000ff,
- orange = 0xffa500ff, // 0xffa500ff,
- purple = 0xC060F0ff,
- red = 0xFF4040ff,
- teal = 0x80f0c0ff,
- yellow = 0xffff00ff,
- black = 0x000000ff,
- darkgrey = 0x808080ff,
- silver = 0xD0D0D0ff,
- grey = 0xC0C0C0ff,
- lightgrey = 0xE8E8E8ff,
- white = 0xffffffff,
- }
-
- public static string ToHtmlString(this RGBA color) => $"{color:X}";
-
- public static string Bold(this string str) => $"{str} ";
-
- public static string Color(this string str, Color color) => $"{str} ";
-
- public static string Color(this string str, RGBA color) => $"{str} ";
-
- public static string Color(this string str, string rrggbbaa) => $"{str} ";
-
- public static string color(this string s, string color) => _ = $"{s} ";
- public static string White(this string s) => _ = s.color("white");
- public static string Grey(this string s) => _ = s.color("#A0A0A0FF");
- public static string Red(this string s) => _ = s.color("#C04040E0");
- public static string Pink(this string s) => _ = s.color("#FFA0A0E0");
- public static string Green(this string s) => _ = s.color("#00ff00ff");
- public static string Blue(this string s) => _ = s.color("blue");
- public static string Cyan(this string s) => _ = s.color("cyan");
- public static string Magenta(this string s) => _ = s.color("magenta");
- public static string Yellow(this string s) => _ = s.color("yellow");
- public static string Orange(this string s) => _ = s.color("orange");
-
-
-
- public static string Italic(this string str) => $"{str} ";
-
- public static string ToSentence(this string str) => Regex.Replace(str, @"((?<=\p{Ll})\p{Lu})|\p{Lu}(?=\p{Ll})", " $0").TrimStart();//return string.Concat(str.Select(c => char.IsUpper(c) ? " " + c : c.ToString())).TrimStart(' ');
-
- public static string Size(this string str, int size) => $"{str} ";
-
- public static string SizePercent(this string str, int percent) => $"{str} ";
- }
-}
diff --git a/ModKit/Utility/Extensions/UnityExtensions.cs b/ModKit/Utility/Extensions/UnityExtensions.cs
deleted file mode 100644
index 82515efd7..000000000
--- a/ModKit/Utility/Extensions/UnityExtensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using UnityEngine;
-
-namespace ModKit.Utility {
- public static class UnityExtensions {
- private static void SafeDestroyInternal(GameObject obj) {
- obj.transform.SetParent(null, false);
- obj.SetActive(false);
- Object.Destroy(obj);
- }
-
- public static void SafeDestroy(this GameObject obj) {
- if (obj) {
- SafeDestroyInternal(obj);
- }
- }
-
- public static void SafeDestroy(this Component obj) {
- if (obj) {
- SafeDestroyInternal(obj.gameObject);
- }
- }
- }
-}
diff --git a/ModKit/Utility/GUISubScope.cs b/ModKit/Utility/GUISubScope.cs
deleted file mode 100644
index fbbdaa697..000000000
--- a/ModKit/Utility/GUISubScope.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using UnityEngine;
-
-namespace ModKit.Utility {
- public class GUISubScope : IDisposable {
- public GUISubScope() : this(null) { }
-
- public GUISubScope(string subtitle) {
- if (!string.IsNullOrEmpty(subtitle))
- GUILayout.Label(subtitle.Bold());
- GUILayout.BeginHorizontal();
- GUILayout.Space(10f);
- GUILayout.BeginVertical();
- }
-
- public void Dispose() {
- GUILayout.EndVertical();
- GUILayout.EndHorizontal();
- }
- }
-}
diff --git a/ModKit/Utility/Search.cs b/ModKit/Utility/Search.cs
deleted file mode 100644
index 0b898146f..000000000
--- a/ModKit/Utility/Search.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace ModKit {
-
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Linq;
-
- namespace BlueprintExplorer {
- public interface ISearchable {
- Dictionary> Providers { get; } // named functions to extract different text out of the target
- Dictionary Matches { get; set; } // place to store search results
- }
- public static class MatchHelpers {
- public static bool HasMatches(this ISearchable searchable, float scoreThreshold = 10) => searchable.Matches == null || searchable.Matches.Where(m => m.Value.IsMatch && m.Value.Score >= scoreThreshold).Any();
- }
- public class MatchResult {
- public struct Span {
- public UInt16 From;
- public UInt16 Length;
-
- public Span(int start, int length = -1) {
- From = (ushort)start;
- Length = (ushort)length;
- }
- public void End(int end) {
- Length = (UInt16)(end - From);
- }
- }
-
- public ISearchable Target;
- public string Key;
- public string Text;
- public MatchQuery Context;
-
- public bool IsMatch => TotalMatched > 0;
- public List spans = new();
- public int MatchedCharacters;
- public int BestRun;
- public int SingleRuns;
- public int TotalMatched;
- public float Penalty;
- public float Bonus;
- public float MatchRatio => TotalMatched / (float)Context.SearchText.Length;
- public float TargetRatio;
- public int GoodRuns => spans.Count(x => x.Length > 2); //> 2);
-
- public float Score => (TargetRatio * MatchRatio * 1.0f) + (BestRun * 4) + (GoodRuns * 2) - Penalty + Bonus;
-
- public MatchResult(ISearchable target, string key, string text, MatchQuery context) {
- Target = target;
- Key = key;
- Text = text;
- this.Context = context;
- }
- public void AddSpan(Span span) {
- spans.Add(span);
-
- //update some stats that get used for scoring
- if (span.Length > BestRun)
- BestRun = span.Length;
- if (span.Length == 1)
- SingleRuns++;
- TotalMatched += span.Length;
- }
- }
- public class MatchQuery {
- public string SearchText; // general search text
- public Dictionary RestrictedSearchTexts; // restricted to certain provider keys
- private MatchResult Match(string searchText, ISearchable searchable, string key, string text) {
- var result = new MatchResult(searchable, key, text, this);
- var index = text.IndexOf(searchText);
- if (index >= 0) {
- var span = new MatchResult.Span(index, searchText.Length);
- result.AddSpan(span);
- result.TargetRatio = result.TotalMatched / (float)text.Length;
- }
- return result;
- }
- private MatchResult FuzzyMatch(ISearchable searchable, string key, string text) {
- var result = new MatchResult(searchable, key, text, this);
-
- var searchTextIndex = 0;
- var targetIndex = -1;
-
- var searchText = result.Context.SearchText;
- var target = result.Text;
-
- // find a common prefix if any, so n:cat h:catsgrace is better than n:cat h:blahcatsgrace
- targetIndex = target.IndexOf(searchText[searchTextIndex]);
- if (targetIndex == 0)
- result.Bonus = 2.0f;
-
- // penalise matches that don't have a common prefix, while increasing searchTextIndex and targetIndex to the first match, so:
- // n:bOb h:hellOworldbob
- // ^ ^
- while (targetIndex == -1 && searchTextIndex < searchText.Length) {
- if (searchTextIndex == 0)
- result.Penalty = 2;
- else
- result.Penalty += result.Penalty * .5f;
- targetIndex = target.IndexOf(searchText[searchTextIndex]);
- searchTextIndex++;
- }
-
- // continue to match the next searchTextIndex greedily in target
- while (searchTextIndex < searchText.Length) {
- // find the next point in target that matches searchIndex:
- // n:bOb h:helloworldBob
- // ^ ^
- targetIndex = target.IndexOf(searchText[searchTextIndex], targetIndex);
- if (targetIndex == -1)
- break;
-
- //continue matching while both are in sync
- var span = new MatchResult.Span(targetIndex);
- while (targetIndex < target.Length && searchTextIndex < searchText.Length && searchText[searchTextIndex] == target[targetIndex]) {
- //if this span is rooted at the start of the word give a bonus because start is most importatn
- if (span.From == 0 && searchTextIndex > 0)
- result.Bonus += result.Bonus;
- searchTextIndex++;
- targetIndex++;
- }
-
- //record the end of the span
- span.End(targetIndex);
- result.AddSpan(span);
- }
- result.TargetRatio = result.TotalMatched / (float)target.Length;
- return result;
- }
-
- public MatchQuery(string queryText) {
- var unrestricted = new List();
- RestrictedSearchTexts = new();
- var terms = queryText.Split(' ');
- foreach (var term in terms) {
- if (term.Contains(':')) {
- var pair = term.Split(':');
- RestrictedSearchTexts[pair[0]] = pair[1];
- }
- else
- unrestricted.Add(term);
- }
- SearchText = string.Join(" ", unrestricted);
- }
-
- public ISearchable Evaluate(ISearchable searchable) {
- if (SearchText?.Length > 0 || RestrictedSearchTexts.Count > 0) {
- searchable.Matches = new();
- foreach (var provider in searchable.Providers) {
- var key = provider.Key;
- var text = provider.Value();
- var foundRestricted = false;
- foreach (var entry in RestrictedSearchTexts) {
- if (key.StartsWith(entry.Key)) {
- searchable.Matches[key] = Match(entry.Value, searchable, key, text);
- foundRestricted = true;
- break;
- }
- }
- if (!foundRestricted && SearchText?.Length > 0)
- searchable.Matches[key] = FuzzyMatch(searchable, key, text);
- }
- }
- else
- searchable.Matches = null;
- return searchable;
- }
- public void UpdateSearchResults(IEnumerable searchables) {
- foreach (var searchable in searchables)
- this.Evaluate(searchable);
- }
- }
-
-#if false
- public static class FuzzyMatcher {
- public static IEnumerable FuzzyMatch(this IEnumerable<(T, string)> input, string needle, float scoreThreshold = 10) {
- var result = new MatchQuery(needle.ToLower());
- return input.Select(i => result.Match(i.Item2, i.Item1)).Where(match => match.Score > scoreThreshold).OrderByDescending(match => match.Score).Select(m => m.Handle);
- }
- ///
- /// Fuzzy Match all items in input against the needle
- ///
- ///
- /// input items
- /// function to get the 'string' key of an input item to match against
- /// value to match against
- /// discard all results under this score (default: 10)
- /// An IEnumerable that contains elements from the input sequence that score above the threshold, sorted by score
- public static IEnumerable FuzzyMatch(this IEnumerable input, Func haystack, string needle, float scoreThreshold = 10) {
- return input.Select(i => (i, haystack(i))).FuzzyMatch(needle, scoreThreshold);
- }
-
-
- public class ExampleType {
- int Foo;
- string Bar;
-
- public string Name => $"{Bar}.{Foo}";
- }
-
- public static void Example() {
- //Assume some input list (or enumerable)
- List inputList = new();
-
- //Get an enumerable of all the matches (above a score threshold, default = 10)
- var matches = inputList.FuzzyMatch(type => type.Name, "string_to_search");
-
- //Get top 20 results
- var top20 = matches.Take(20).ToList();
- }
-
- }
-#endif
- }
-}
diff --git a/ModKit/Utility/Translator.cs b/ModKit/Utility/Translator.cs
deleted file mode 100644
index f3f2687a2..000000000
--- a/ModKit/Utility/Translator.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace ModKit {
- public static class Translater {
- public static Dictionary cachedTranslations = new();
-
-#if true
- public static async Task MassTranslate(List strings) {
- using (var client = new HttpClient()) {
- var fromLanguage = "ru";//Russian
- var toLanguage = "en";//English
- var text = String.Join(" | ", strings);
- Mod.Log($"{text}");
- var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t";
- // Serialize our concrete class into a JSON String
- var stringPayload = JsonConvert.SerializeObject(text);
- var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
- var response = await client.PostAsync(url, content);
- var result = await response.Content.ReadAsStringAsync();
- // how do I best ask for this ^^^ and parse it out?
- // store the text => translation in the cachedTranslations dict
- }
- }
-
-#else
-
- private const int maxQuerySize = 2000;
- public static async Task MassTranslate(List strings) {
- using (var client = new HttpClient()) {
- string accum = "";
- foreach (var text in strings) {
- if (accum.Length + text.Length < maxQuerySize - 4) {
- accum += $"\\{text}//";
- }
- else {
- RawMassTranslate(accum);
- accum = "";
- }
- }
- if (accum.Length > 0) {
- RawMassTranslate(accum);
- accum = "";
- }
- }
- }
- private static void RawMassTranslate(string text) {
- var fromLanguage = "ru";//Russian
- var toLanguage = "en";//English
- var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={Uri.EscapeDataString(text)}";
- var webClient = new WebClient {
- Encoding = System.Text.Encoding.UTF8
- };
- try {
- var result = webClient.DownloadString(url);
- Mod.Log($"response: {result}");
- result = result.Substring(4, result.IndexOf("\"", 4, StringComparison.Ordinal) - 4);
-
- cachedTranslations[text] = result;
- }
- catch (Exception e) {
- Mod.Log(url);
- Mod.Error(e);
- }
- }
-#endif
- public static String Translate(this string text) {
- if (cachedTranslations.TryGetValue(text, out var value))
- return value;
-#if true
- var fromLanguage = "ru";//Russian
- var toLanguage = "en";//English
- var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={Uri.EscapeDataString(text)}";
- var webClient = new WebClient {
- Encoding = Encoding.UTF8
- };
- try {
- var result = webClient.DownloadString(url);
- result = result.Substring(4, result.IndexOf("\"", 4, StringComparison.Ordinal) - 4);
- cachedTranslations[text] = result;
- return result;
- }
- catch (Exception e) {
- Mod.Log(url);
- Mod.Error(e);
- return text;
- }
-#else
- var toLanguage = "en";
- var fromLanguage = "ru";
- var uriBuilder = new UriBuilder {
- Scheme = "https",
- Host = "translate.googleapis.com",
- Path = "translate_a/single",
- Query = $"client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={Uri.EscapeDataString(text)}"
- };
- var request = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
-
- var response = (HttpWebResponse)request.GetResponse();
- Mod.Log($"response: {response}");
- var translation = new StreamReader(response.GetResponseStream()).ReadToEnd();
- Mod.Log($"{text} => {translation}");
- return translation;
-#endif
-
-#if true
-#else
- var toLanguage = "en";
- var fromLanguage = "ru";
- var inputText = "Все люди рождаются свободными и равными в своем достоинстве и правах. Они наделены разумом и совестью и должны поступать в отношении друг друга в духе братства.";
-
- var uriBuilder = new UriBuilder
- {
- Scheme = "https",
- Host = "translate.googleapis.com",
- Path = "translate_a/single",
- Query = $"client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={Uri.EscapeDataString(inputText)}"
- };
- var request = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
-
- var response = (HttpWebResponse)request.GetResponse();
-
- var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
-
- Console.WriteLine(responseString);
-
-
- -----------------
- var fromLanguage = "ru";//Russian
- var toLanguage = "en";//English
- var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={Uri.EscapeDataString(text)}";
- var webClient = new WebClient {
- Encoding = System.Text.Encoding.UTF8
- };
- try {
- var result = webClient.DownloadString(url);
- result = result.Substring(4, result.IndexOf("\"", 4, StringComparison.Ordinal) - 4);
- cachedTranslations[text] = result;
- return result;
- }
- catch (Exception e) {
- Mod.Log(url);
- Mod.Error(e);
- return text;
- }
-
- -----------------
-
- var request = (HttpWebRequest)WebRequest.Create("http://www.example.com/recepticle.aspx");
-
-var response = (HttpWebResponse)request.GetResponse();
-
-var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
-
-
- var toLanguage = "en";//English
- var fromLanguage = "ru";//Russian
- var url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={fromLanguage}&tl={toLanguage}&dt=t&q={HttpUtility.UrlEncode(text)}";
- var webClient = new WebClient {
- Encoding = System.Text.Encoding.UTF8
- };
- var result = webClient.DownloadString(url);
- try {
- result = result.Substring(4, result.IndexOf("\"", 4, StringComparison.Ordinal) - 4);
- return result;
- }
- catch {
- return "Error";
- }
-#endif
- }
- }
-}
diff --git a/ModKit/Utility/Utilities.cs b/ModKit/Utility/Utilities.cs
deleted file mode 100644
index 9924decfa..000000000
--- a/ModKit/Utility/Utilities.cs
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright < 2021 > Narria (github user Cabarius) - License: MIT
-using HarmonyLib;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-
-namespace ModKit
-{
- public static class Utilities
- {
- public static TValue GetValueOrDefault(this Dictionary dictionary, TKey key, TValue defaultValue = default)
- {
- if (dictionary == null) { throw new ArgumentNullException(nameof(dictionary)); } // using C# 6
- if (key == null) { throw new ArgumentNullException(nameof(key)); } // using C# 6
-
- return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
- }
- public static Dictionary ToDictionaryIgnoringDuplicates(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer = null) {
- if (source == null)
- throw new ArgumentException("source");
- if (keySelector == null)
- throw new ArgumentException("keySelector");
- if (elementSelector == null)
- throw new ArgumentException("elementSelector");
- Dictionary d = new Dictionary(comparer);
- foreach (TSource element in source) {
- if (!d.ContainsKey(keySelector(element)))
- d.Add(keySelector(element), elementSelector(element));
- }
- return d;
- }
- public static object GetPropValue(this object obj, string name)
- {
- foreach (var part in name.Split('.'))
- {
- if (obj == null) { return null; }
-
- var type = obj.GetType();
- var info = type.GetProperty(part);
- if (info == null) { return null; }
-
- obj = info.GetValue(obj, null);
- }
- return obj;
- }
- public static T GetPropValue(this object obj, string name)
- {
- var retval = GetPropValue(obj, name);
- if (retval == null) { return default; }
- // throws InvalidCastException if types are incompatible
- return (T)retval;
- }
- public static object SetPropValue(this object obj, string name, object value)
- {
- var parts = name.Split('.');
- var final = parts.Last();
- if (final == null)
- return null;
- foreach (var part in parts)
- {
- if (obj == null) { return null; }
- var type = obj.GetType();
- var info = type.GetProperty(part);
- if (info == null) { return null; }
- if (part == final)
- {
- info.SetValue(obj, value);
- return value;
- }
- else
- {
- obj = info.GetValue(obj, null);
- }
- }
- return null;
- }
- public static T SetPropValue(this object obj, string name, T value)
- {
- object retval = SetPropValue(obj, name, value);
- if (retval == null) { return default; }
- // throws InvalidCastException if types are incompatible
- return (T)retval;
- }
- public static string StripHTML(this string s) => Regex.Replace(s, "<.*?>", string.Empty);
- public static string UnityRichTextToHtml(string s)
- {
- s = s.Replace("", "");
- s = s.Replace("", "");
- s += " ";
-
- return s;
- }
- public static string MergeSpaces(this string str, bool trim = false) {
- if (str == null)
- return null;
- else {
- StringBuilder stringBuilder = new StringBuilder(str.Length);
-
- int i = 0;
- foreach (char c in str) {
- if (c != ' ' || i == 0 || str[i - 1] != ' ')
- stringBuilder.Append(c);
- i++;
- }
- if (trim)
- return stringBuilder.ToString().Trim();
- else
- return stringBuilder.ToString();
- }
- }
- public static string ReplaceLastOccurrence(this string Source, string Find, string Replace)
- {
- int place = Source.LastIndexOf(Find);
-
- if (place == -1)
- return Source;
-
- string result = Source.Remove(place, Find.Length).Insert(place, Replace);
- return result;
- }
- public static string[] getObjectInfo(object o)
- {
-
- var fields = "";
- foreach (var field in Traverse.Create(o).Fields())
- {
- fields = fields + field + ", ";
- }
- var methods = "";
- foreach (var method in Traverse.Create(o).Methods())
- {
- methods = methods + method + ", ";
- }
- var properties = "";
- foreach (var property in Traverse.Create(o).Properties())
- {
- properties = properties + property + ", ";
- }
- return new string[] { fields, methods, properties };
- }
- public static string SubstringBetweenCharacters(this string input, char charFrom, char charTo)
- {
- var posFrom = input.IndexOf(charFrom);
- if (posFrom != -1) //if found char
- {
- var posTo = input.IndexOf(charTo, posFrom + 1);
- if (posTo != -1) //if found char
- {
- return input.Substring(posFrom + 1, posTo - posFrom - 1);
- }
- }
-
- return string.Empty;
- }
- public static string[] TrimCommonPrefix(this string[] values)
- {
- var prefix = string.Empty;
- int? resultLength = null;
-
- if (values != null)
- {
- if (values.Length > 1)
- {
- var min = values.Min(value => value.Length);
- for (var charIndex = 0; charIndex < min; charIndex++)
- {
- for (var valueIndex = 1; valueIndex < values.Length; valueIndex++)
- {
- if (values[0][charIndex] != values[valueIndex][charIndex])
- {
- resultLength = charIndex;
- break;
- }
- }
- if (resultLength.HasValue)
- {
- break;
- }
- }
- if (resultLength.HasValue &&
- resultLength.Value > 0)
- {
- prefix = values[0].Substring(0, resultLength.Value);
- }
- }
- else if (values.Length > 0)
- {
- prefix = values[0];
- }
- }
- return prefix.Length > 0 ? values.Select(s => s.Replace(prefix, "")).ToArray() : values;
- }
-
- public static Dictionary NameToValueDictionary(this TEnum enumValue) where TEnum : struct
- {
- var enumType = enumValue.GetType();
- return Enum.GetValues(enumType)
- .Cast()
- .ToDictionary(e => Enum.GetName(enumType, e), e => e);
- }
- public static Dictionary ValueToNameDictionary(this TEnum enumValue) where TEnum : struct
- {
- var enumType = enumValue.GetType();
- return Enum.GetValues(enumType)
- .Cast()
- .ToDictionary(e => e, e => Enum.GetName(enumType, e));
- }
- public static Dictionary Filter(this Dictionary dict,
- Predicate> predicate) => dict.Where(it => predicate(it)).ToDictionary(it => it.Key, it => it.Value);
- public static List> Partition(this List values, int chunkSize) {
- return values.Select((x, i) => new { Index = i, Value = x })
- .GroupBy(x => x.Index / chunkSize)
- .Select(x => x.Select(v => v.Value).ToList())
- .ToList();
- }
- public static List> BuildChunksWithIteration(this List fullList, int batchSize) {
- var chunkedList = new List>();
- var temporary = new List();
- for (int i = 0; i < fullList.Count; i++) {
- //Mod.Log($"{i}/{fullList.Count}");
- var e = fullList[i];
- if (temporary.Count < batchSize) {
- temporary.Add(e);
- }
- else {
- //Mod.Log("new row");
- chunkedList.Add(temporary);
- temporary = new List() { e };
- }
-
- if (i == fullList.Count - 1) {
- //Mod.Log("last row");
- chunkedList.Add(temporary);
- }
- }
- //Mod.Log($"result - rows: {chunkedList.Count}");
- return chunkedList;
- }
- // Chunk
- public static IEnumerable Chunk(this IEnumerable source, int size) {
- if (source == null) {
- throw new ArgumentNullException("source");
- // ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
- }
-
- if (size < 1) {
- throw new ArgumentOutOfRangeException("size < 1");
- // ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.size);
- }
-
- return ChunkIterator(source, size);
- }
-
- private static IEnumerable ChunkIterator(IEnumerable source, int size) {
- using IEnumerator e = source.GetEnumerator();
-
- // Before allocating anything, make sure there's at least one element.
- if (e.MoveNext()) {
- // Now that we know we have at least one item, allocate an initial storage array. This is not
- // the array we'll yield. It starts out small in order to avoid significantly overallocating
- // when the source has many fewer elements than the chunk size.
- int arraySize = Math.Min(size, 4);
- int i;
- do {
- var array = new TSource[arraySize];
-
- // Store the first item.
- array[0] = e.Current;
- i = 1;
-
- if (size != array.Length) {
- // This is the first chunk. As we fill the array, grow it as needed.
- for (; i < size && e.MoveNext(); i++) {
- if (i >= array.Length) {
- arraySize = (int)Math.Min((uint)size, 2 * (uint)array.Length);
- Array.Resize(ref array, arraySize);
- }
-
- array[i] = e.Current;
- }
- }
- else {
- // For all but the first chunk, the array will already be correctly sized.
- // We can just store into it until either it's full or MoveNext returns false.
- TSource[] local = array; // avoid bounds checks by using cached local (`array` is lifted to iterator object as a field)
- //Debug.Assert(local.Length == size);
- for (; (uint)i < (uint)local.Length && e.MoveNext(); i++) {
- local[i] = e.Current;
- }
- }
-
- if (i != array.Length) {
- Array.Resize(ref array, i);
- }
-
- yield return array;
- }
- while (i >= size && e.MoveNext());
- }
- }
- }
- public static class MK
- {
- public static bool IsKindOf(this Type type, Type baseType) => type.IsSubclassOf(baseType) || type == baseType;
- }
- public static class CloneUtil
- {
- private static readonly Func clone;
-
- static CloneUtil()
- {
- var cloneMethod = typeof(T).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
- clone = (Func)cloneMethod.CreateDelegate(typeof(Func));
- }
-
- public static T ShallowClone(T obj) => (T)clone(obj);
- }
-
- public static class CloneUtil
- {
- public static T ShallowClone(this T obj) => CloneUtil.ShallowClone(obj);
- }
-}
diff --git a/ModKit/packages.config b/ModKit/packages.config
deleted file mode 100644
index f0f60a9e4..000000000
--- a/ModKit/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/ToyBox.sln b/ToyBox.sln
index 57c468929..0cc90ca39 100644
--- a/ToyBox.sln
+++ b/ToyBox.sln
@@ -1,31 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31105.61
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToyBox", "ToyBox\ToyBox.csproj", "{22DCB4E1-D979-4EA9-913A-4EE1634B4DED}"
-EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ModKitSrc", "ModKit\ModKitSrc.shproj", "{7D3F9428-C134-47FE-9D04-305B4D330401}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4AA27760-A0FF-4EFA-B330-CCE957361EDD}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToyBoxRT", "ToyBox\ToyBoxRT.csproj", "{4D9CDA59-3942-46EF-9EBE-FF0498DF5ABB}"
+EndProject
Global
- GlobalSection(SharedMSBuildProjectFiles) = preSolution
- ModKit\ModKitSrc.projitems*{22dcb4e1-d979-4ea9-913a-4ee1634b4ded}*SharedItemsImports = 4
- ModKit\ModKitSrc.projitems*{7d3f9428-c134-47fe-9d04-305b4d330401}*SharedItemsImports = 13
- EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {22DCB4E1-D979-4EA9-913A-4EE1634B4DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {22DCB4E1-D979-4EA9-913A-4EE1634B4DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {22DCB4E1-D979-4EA9-913A-4EE1634B4DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {22DCB4E1-D979-4EA9-913A-4EE1634B4DED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D9CDA59-3942-46EF-9EBE-FF0498DF5ABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D9CDA59-3942-46EF-9EBE-FF0498DF5ABB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D9CDA59-3942-46EF-9EBE-FF0498DF5ABB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D9CDA59-3942-46EF-9EBE-FF0498DF5ABB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.png
deleted file mode 100644
index a0a1f5b6e..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.pxd
deleted file mode 100644
index 287e243b6..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.png
deleted file mode 100644
index 7cd0af642..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.pxd
deleted file mode 100644
index 1bc842eb0..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_dark_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.png
deleted file mode 100644
index 819b55b44..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.pxd
deleted file mode 100644
index 91b836477..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_m.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.png
deleted file mode 100644
index c2e8c352e..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.pxd
deleted file mode 100644
index 2f52da8d1..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_cian_n.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.png
deleted file mode 100644
index 02fcb4485..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.pxd
deleted file mode 100644
index 501ff5992..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.png
deleted file mode 100644
index e1a9248df..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.pxd
deleted file mode 100644
index 73090f1ae..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_green_dark_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.png
deleted file mode 100644
index 7b5c9b8ff..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.pxd
deleted file mode 100644
index 1f4d51d2f..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.png
deleted file mode 100644
index a40b82c88..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.pxd
deleted file mode 100644
index 49f7c3e84..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_purple_dark_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.png
deleted file mode 100644
index d77b83513..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.pxd
deleted file mode 100644
index 11c0473a9..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.png
deleted file mode 100644
index b927c908b..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.pxd
deleted file mode 100644
index eae052e2e..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_red_dark_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.png
deleted file mode 100644
index 11e3998c1..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.pxd
deleted file mode 100644
index 71eef7435..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_d.pxd and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.png b/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.png
deleted file mode 100644
index 7c2bf0abc..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.png and /dev/null differ
diff --git a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.pxd b/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.pxd
deleted file mode 100644
index 79e13489b..000000000
Binary files a/ToyBox/Art/Texture2D/areshkagal_puzzle_yellow_dark_d.pxd and /dev/null differ
diff --git a/ToyBox/classes/Infrastructure/AssetLoader.cs b/ToyBox/Classes/Infrastructure/AssetLoader.cs
similarity index 57%
rename from ToyBox/classes/Infrastructure/AssetLoader.cs
rename to ToyBox/Classes/Infrastructure/AssetLoader.cs
index 19dcd2eef..bc88da08d 100644
--- a/ToyBox/classes/Infrastructure/AssetLoader.cs
+++ b/ToyBox/Classes/Infrastructure/AssetLoader.cs
@@ -1,10 +1,12 @@
using System.IO;
-using TMPro;
using UnityEngine;
using ModKit;
+using HarmonyLib;
+using System;
namespace ToyBox {
class AssetLoader {
+ private static Lazy> LoadImage = new(() => AccessTools.MethodDelegate>(AccessTools.Method(typeof(ImageConversion), nameof(ImageConversion.LoadImage), [typeof(Texture2D), typeof(byte[])])));
public static Sprite LoadInternal(string folder, string file, Vector2Int size) {
return Image2Sprite.Create($"{Mod.modEntry.Path}Assets{Path.DirectorySeparatorChar}{folder}{Path.DirectorySeparatorChar}{file}", size);
}
@@ -14,29 +16,11 @@ public static class Image2Sprite {
public static Sprite Create(string filePath, Vector2Int size) {
var bytes = File.ReadAllBytes(icons_folder + filePath);
var texture = new Texture2D(size.x, size.y, TextureFormat.ARGB32, false);
- _ = texture.LoadImage(bytes);
+ // After the latest Unity update, UnityEngine.ImageConversionModule is targetting .NET standard 2.1 and using System.ReadOnlySpan
+ // Referencing System.Memory does not help; so I just created this runtime delegate. Since this method is (as of now) unused anyways it's a good enough fix.
+ _ = LoadImage.Value(texture, bytes);
return Sprite.Create(texture, new Rect(0, 0, size.x, size.y), new Vector2(0, 0));
}
}
}
-
- public struct ModIcon {
- private readonly string name;
- private readonly Vector2Int size;
-
- public ModIcon(string name, int w, int h) {
- _Sprite = null;
- this.name = name;
- this.size = new Vector2Int(w, h);
- }
- public ModIcon(string name, Vector2Int size) {
- _Sprite = null;
- this.name = name;
- this.size = size;
- }
-
- private Sprite _Sprite;
- public Sprite Sprite => _Sprite ??= (AssetLoader.LoadInternal("icons", name + ".png", size) ?? AssetLoader.LoadInternal("icons", "missing", new Vector2Int(32, 32)));
-
- }
}
diff --git a/ToyBox/classes/Infrastructure/UnitEntityDataUtils.cs b/ToyBox/Classes/Infrastructure/BaseUnitDataUtils.cs
similarity index 53%
rename from ToyBox/classes/Infrastructure/UnitEntityDataUtils.cs
rename to ToyBox/Classes/Infrastructure/BaseUnitDataUtils.cs
index 3409ee46f..9ac290a4b 100644
--- a/ToyBox/classes/Infrastructure/UnitEntityDataUtils.cs
+++ b/ToyBox/Classes/Infrastructure/BaseUnitDataUtils.cs
@@ -1,30 +1,32 @@
// borrowed shamelessly and enhanced from Bag of Tricks https://www.nexusmods.com/pathfinderkingmaker/mods/26, which is under the MIT Licenseusing Kingmaker;
-using System.Collections.Generic;
-using System.Linq;
using Kingmaker;
-using Kingmaker.Blueprints.Classes.Selection;
+using Kingmaker.Blueprints;
+using Kingmaker.Blueprints.Classes;
using Kingmaker.Blueprints.Facts;
+using Kingmaker.Blueprints.Items.Components;
+using Kingmaker.Blueprints.Items.Equipment;
+using Kingmaker.Blueprints.Root.Strings;
using Kingmaker.Cheats;
+using Kingmaker.Controllers.Combat;
using Kingmaker.Designers;
+using Kingmaker.ElementsSystem;
using Kingmaker.EntitySystem.Entities;
using Kingmaker.GameModes;
+using Kingmaker.Items;
+using Kingmaker.UI.Common;
using Kingmaker.UnitLogic;
using Kingmaker.UnitLogic.Buffs.Blueprints;
using Kingmaker.UnitLogic.Mechanics;
-using Kingmaker.Utility;
using Kingmaker.UnitLogic.Parts;
-using Kingmaker.ElementsSystem;
+using Kingmaker.Utility;
using ModKit;
-using Kingmaker.Blueprints.Classes;
-using Kingmaker.Items;
-using Kingmaker.Controllers.Combat;
-using Utilities = Kingmaker.Cheats.Utilities;
-using Kingmaker.Blueprints.Items.Components;
-using Kingmaker.Blueprints;
using ModKit.Utility;
-using Kingmaker.Blueprints.Items.Equipment;
-using Kingmaker.Blueprints.Root.Strings;
-using Kingmaker.UI.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Warhammer.SpaceCombat.StarshipLogic;
+using Utilities = Kingmaker.Cheats.Utilities;
namespace ToyBox {
public enum UnitSelectType {
@@ -34,188 +36,120 @@ public enum UnitSelectType {
Friendly,
Enemies,
Everyone,
+ Ship,
+ EnemyShip,
}
- public static class UnitEntityDataUtils {
+ public static class BaseUnitDataUtils {
public static Settings settings => Main.Settings;
- public static float GetMaxSpeed(List data) => data.Select(u => u.ModifiedSpeedMps).Max();
-
- public static bool CheckUnitEntityData(UnitEntityData unitEntityData, UnitSelectType selectType) {
- if (unitEntityData == null) return false;
+ public static float GetMaxSpeed(List data) => Shodan.GetMaxSpeed(data);
+ public static bool CheckUnitEntityData(BaseUnitEntity baseUnitEntity, UnitSelectType selectType) {
+ if (baseUnitEntity == null) return false;
switch (selectType) {
case UnitSelectType.Everyone:
return true;
case UnitSelectType.Party:
- if (unitEntityData.IsPlayerFaction) {
+ if (baseUnitEntity.IsPlayerFaction) {
return true;
}
return false;
case UnitSelectType.You:
- if (unitEntityData.IsMainCharacter) {
+ if (baseUnitEntity.IsMainCharacter) {
return true;
}
return false;
case UnitSelectType.Friendly:
- return !unitEntityData.IsEnemy(GameHelper.GetPlayerCharacter());
+ return !baseUnitEntity.IsEnemy();
case UnitSelectType.Enemies:
- // TODO - should this be IsEnemy instead?
- if (!unitEntityData.IsPlayerFaction && unitEntityData.Descriptor.AttackFactions.Contains(Game.Instance.BlueprintRoot.PlayerFaction)) {
- return true;
- }
- return false;
+ return baseUnitEntity.IsEnemy();
+ case UnitSelectType.Ship:
+ return baseUnitEntity.IsStarship() && !baseUnitEntity.IsEnemy();
+ case UnitSelectType.EnemyShip:
+ return baseUnitEntity.IsStarship() && baseUnitEntity.IsEnemy();
default:
return false;
}
}
- public static void Kill(UnitEntityData unit) => unit.Descriptor.Damage = unit.Descriptor.Stats.HitPoints.ModifiedValue + unit.Descriptor.Stats.TemporaryHitPoints.ModifiedValue;
-
- public static void ForceKill(UnitEntityData unit) => unit.Descriptor.State.ForceKill = true;
+ public static void Kill(BaseUnitEntity unit) => unit.Health.Damage = unit.Stats.GetStat(StatType.HitPoints) + unit.Stats.GetStat(StatType.TemporaryHitPoints);
- public static void ResurrectAndFullRestore(UnitEntityData unit) => unit.Descriptor.ResurrectAndFullRestore();
-
- public static void Buff(UnitEntityData unit, string buffGuid) => unit.Descriptor.AddFact((BlueprintUnitFact)Utilities.GetBlueprintByGuid(buffGuid),
- (MechanicsContext)null,
- new FeatureParam());
-
- public static void Charm(UnitEntityData unit) {
- if (unit != null)
- unit.Descriptor.SwitchFactions(Game.Instance.BlueprintRoot.PlayerFaction, true);
- else
+ public static void Charm(BaseUnitEntity unit) {
+ if (unit != null) {
+ // TODO: can we still do this?
+ // unit.SetFaction() = Game.Instance.BlueprintRoot.PlayerFaction;
+ } else
Mod.Warn("Unit is null!");
}
-
- public static void AddToParty(UnitEntityData unit) {
+ public static void AddToParty(BaseUnitEntity unit) {
Charm(unit);
Game.Instance.Player.AddCompanion(unit);
}
-#if true
- public static void AddCompanion(UnitEntityData unit) {
+ public static void AddCompanion(BaseUnitEntity unit) {
var currentMode = Game.Instance.CurrentMode;
Game.Instance.Player.AddCompanion(unit);
if (currentMode == GameModeType.Default || currentMode == GameModeType.Pause) {
- var pets = unit.Pets;
unit.IsInGame = true;
- unit.Position = Game.Instance.Player.MainCharacter.Value.Position;
- unit.LeaveCombat();
+ unit.Position = Game.Instance.Player.MainCharacter.Entity.Position;
+ unit.CombatState.LeaveCombat();
Charm(unit);
- var unitPartCompanion = unit.Get();
- unitPartCompanion.State = CompanionState.InParty;
+ var unitPartCompanion = unit.GetAll();
+ Game.Instance.Player.AddCompanion(unit);
if (unit.IsDetached) {
Game.Instance.Player.AttachPartyMember(unit);
}
- foreach (var pet in pets) {
- pet.Entity.Position = unit.Position;
- }
}
}
- public static void RecruitCompanion(UnitEntityData unit) {
+ public static void RecruitCompanion(BaseUnitEntity unit) {
var currentMode = Game.Instance.CurrentMode;
- unit = Game.Instance.EntityCreator.RecruitNPC(unit, unit.Blueprint);
+ unit = GameHelper.RecruitNPC(unit, unit.Blueprint);
// this line worries me but the dev said I should do it
//unit.HoldingState.RemoveEntityData(unit);
//player.AddCompanion(unit);
if (currentMode == GameModeType.Default || currentMode == GameModeType.Pause) {
- var pets = unit.Pets;
+ var pets = Game.Instance.Player.PartyAndPets.Where(u => u.IsPet && u.OwnerEntity == unit);
unit.IsInGame = true;
- unit.Position = Game.Instance.Player.MainCharacter.Value.Position;
- unit.LeaveCombat();
+ unit.Position = Shodan.MainCharacter.Position;
+ unit.CombatState.LeaveCombat();
Charm(unit);
- unit.SwitchFactions(Game.Instance.Player.MainCharacter.Value.Faction);
//unit.GroupId = Game.Instance.Player.MainCharacter.Value.GroupId;
//Game.Instance.Player.CrossSceneState.AddEntityData(unit);
if (unit.IsDetached) {
Game.Instance.Player.AttachPartyMember(unit);
}
foreach (var pet in pets) {
- pet.Entity.Position = unit.Position;
- }
- }
- }
- public static void maybeKill(UnitCombatState unitCombatState) {
-
- if (settings.togglekillOnEngage) {
- List partyUnits = Game.Instance.Player.m_PartyAndPets;
- UnitEntityData unit = unitCombatState.Unit;
- if (unit.IsPlayersEnemy && !partyUnits.Contains(unit)) {
- CheatsCombat.KillUnit(unit);
- }
- }
- }
-#else
-note this code from Owlcat
- private static void RecruitCompanion(string parameters)
- {
- string paramString = Utilities.GetParamString(parameters, 1, (string) null);
- bool? paramBool = Utilities.GetParamBool(parameters, 2, (string) null);
- BlueprintUnit blueprint = Utilities.GetBlueprint(paramString);
- if (blueprint == null)
- PFLog.SmartConsole.Log("Cant get companion with name '" + paramString + "'", (object[]) Array.Empty());
- else if (!paramBool.HasValue || paramBool.Value)
- {
- UnitEntityData unitVacuum = Game.Instance.CreateUnitVacuum(blueprint);
- Game.Instance.State.PlayerState.CrossSceneState.AddEntityData((EntityDataBase) unitVacuum);
- unitVacuum.IsInGame = false;
- unitVacuum.Ensure().SetState(CompanionState.ExCompanion);
- }
- else
- {
- Vector3 position = Game.Instance.Player.MainCharacter.Value.Position;
- SceneEntitiesState crossSceneState = Game.Instance.State.PlayerState.CrossSceneState;
- UnitEntityData unit = Game.Instance.EntityCreator.SpawnUnit(blueprint, position, Quaternion.identity, crossSceneState);
- Game.Instance.Player.AddCompanion(unit);
- EventBus.RaiseEvent((Action) (h => h.HandleAddCompanion(unit)));
- }
- }
-
-
- public static void AddCompanion(UnitEntityData unit) {
- Player player = Game.Instance.Player;
- player.AddCompanion(unit);
- GameModeType currentMode = Game.Instance.CurrentMode;
- if (currentMode == GameModeType.Default || currentMode == GameModeType.Pause) {
- var pets = unit.Pets;
- unit.IsInGame = true;
- unit.Position = Game.Instance.Player.MainCharacter.Value.Position;
- unit.LeaveCombat();
- Charm(unit);
- UnitPartCompanion unitPartCompanion = unit.Get();
- unit.Ensure().SetState(CompanionState.InParty);
- unit.SwitchFactions(Game.Instance.Player.MainCharacter.Value.Faction);
- unit.GroupId = Game.Instance.Player.MainCharacter.Value.GroupId;
- unit.HoldingState.RemoveEntityData(unit);
- Game.Instance.Player.CrossSceneState.AddEntityData(unit);
- foreach (var pet in pets) {
- pet.Entity.Position = unit.Position;
+ pet
+ .Position = unit.Position;
}
}
}
-#endif
-
- public static void RemoveCompanion(UnitEntityData unit) {
- _ = Game.Instance.CurrentMode;
- Game.Instance.Player.RemoveCompanion(unit);
+ public static bool IsPartyOrPetInterface(this IBaseUnitEntity unit) {
+ return (unit as BaseUnitEntity)?.IsPartyOrPet() ?? false;
}
-
- public static bool IsPartyOrPet(this UnitDescriptor unit) {
- if (unit?.Unit?.OriginalBlueprint == null || Game.Instance.Player?.AllCharacters == null || Game.Instance.Player?.AllCharacters.Count == 0) {
+ public static bool IsPartyOrPet(this MechanicEntity unit) {
+ if (unit?
+ .OriginalBlueprint == null
+ || Game.Instance.Player?.AllCharacters == null
+ || Game.Instance.Player?.AllCharacters.Count == 0) {
return false;
}
return Game.Instance.Player.AllCharacters
- .Any(x => x.OriginalBlueprint == unit.Unit.OriginalBlueprint && (x.Master == null || x.Master.OriginalBlueprint == null || Game.Instance.Player.AllCharacters.Any(y => y.OriginalBlueprint == x.Master.OriginalBlueprint)));
- }
-
- public static bool TryGetPartyMemberForLevelUpVersion(this UnitDescriptor levelUpUnit, out UnitEntityData ch) {
- ch = Game.Instance?.Player?.AllCharacters.Find(c => c.CharacterName == levelUpUnit.CharacterName);
- return ch != null;
+ .Any(x => x.OriginalBlueprint == unit
+ .OriginalBlueprint
+ && (x.Master == null
+ || x.Master.OriginalBlueprint == null
+ || Game.Instance.Player.AllCharacters.Any(
+ y => y.OriginalBlueprint == x.Master.OriginalBlueprint)
+ )
+ );
}
- public static bool TryGetClass(this UnitEntityData unit, BlueprintCharacterClass cl, out ClassData cd) {
- cd = unit.Progression.Classes.Find(c => c.CharacterClass == cl);
- return cd != null;
+ public static void RemoveCompanion(BaseUnitEntity unit) {
+ _ = Game.Instance.CurrentMode;
+ Game.Instance.Player.RemoveCompanion(unit);
}
+#if false
public static string GetEquipmentRestrictionCaption(this EquipmentRestriction restriction) {
switch (restriction) {
case EquipmentRestrictionAlignment era: return $"Alignment must be {era.Alignment}";
@@ -283,5 +217,6 @@ public static string GetCantEquipReasonText(this ItemEntity item) {
}
return null;
}
+#endif
}
}
diff --git a/ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensions.cs b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensions.cs
new file mode 100644
index 000000000..23d6dcf3a
--- /dev/null
+++ b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensions.cs
@@ -0,0 +1,362 @@
+// Copyright < 2021 > Narria (github user Cabarius) - License: MIT
+using HarmonyLib;
+using Kingmaker.AreaLogic.Etudes;
+using Kingmaker.Blueprints;
+using Kingmaker.Blueprints.Area;
+using Kingmaker.Blueprints.Items;
+using Kingmaker.Blueprints.Items.Ecnchantments;
+using Kingmaker.Code.Blueprints.Quests;
+using Kingmaker.ElementsSystem;
+using Kingmaker.UnitLogic.Buffs.Blueprints;
+using Kingmaker.UnitLogic.Mechanics.Blueprints;
+using ModKit;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace ToyBox {
+
+ public static partial class BlueprintExtensions {
+ public static Settings Settings => Main.Settings;
+
+ private static ConditionalWeakTable> _cachedCollationNames = new() { };
+ private static readonly HashSet BadList = new();
+ public static Dictionary descriptionCache = new();
+ internal static bool wasIncludeInternalNameForTitle = false;
+ public static Dictionary titleCache = new();
+ internal static bool wasIncludeInternalNameForSearchKey = false;
+ public static Dictionary searchKeyCache = new();
+ internal static bool wasIncludeInternalNameForSortKey = false;
+ public static Dictionary sortKeyCache = new();
+ public static void ResetCollationCache() => _cachedCollationNames = new ConditionalWeakTable> { };
+ private static void AddOrUpdateCachedNames(SimpleBlueprint bp, List names) {
+ names = names.Distinct().ToList();
+ if (_cachedCollationNames.TryGetValue(bp, out _)) {
+ _cachedCollationNames.Remove(bp);
+ //Mod.Log($"removing: {bp.NameSafe()}");
+ }
+ _cachedCollationNames.Add(bp, names);
+ //Mod.Log($"adding: {bp.NameSafe()} - {names.Count} - {String.Join(", ", names)}");
+ }
+
+ public static string GetDisplayName(this SimpleBlueprint bp) => bp switch {
+ BlueprintAbilityResource abilityResource => abilityResource.Name,
+ BlueprintArchetype archetype => archetype.Name,
+#pragma warning disable CS0612 // Type or member is obsolete
+ BlueprintCharacterClass charClass => charClass.Name,
+#pragma warning restore CS0612 // Type or member is obsolete
+ BlueprintItem item => item.Name,
+ BlueprintItemEnchantment enchant => enchant.Name,
+ BlueprintMechanicEntityFact fact => fact.NameSafe(),
+ SimpleBlueprint blueprint => blueprint.name,
+ _ => "n/a"
+ };
+ public static string GetDisplayName(this BlueprintSpellbook bp) {
+ var name = bp.DisplayName;
+ if (string.IsNullOrEmpty(name)) name = bp.name.Replace("Spellbook", "");
+ return name;
+ }
+ public static string GetTitle(SimpleBlueprint blueprint, Func formatter = null) {
+ if (titleCache.TryGetValue(blueprint.AssetGuid, out var ret) && (wasIncludeInternalNameForTitle == Settings.showDisplayAndInternalNames)) {
+ return ret;
+ } else {
+ wasIncludeInternalNameForTitle = Settings.showDisplayAndInternalNames;
+ }
+ if (formatter == null) formatter = s => s;
+ if (blueprint is IUIDataProvider uiDataProvider) {
+ string name;
+ bool isEmpty = true;
+ try {
+ isEmpty = string.IsNullOrEmpty(uiDataProvider.Name);
+ } catch (Exception ex) {
+ Mod.Debug($"Error while getting name for {uiDataProvider}: {ex}");
+ }
+ if (isEmpty) {
+ name = blueprint.name;
+ } else {
+ if (blueprint is BlueprintSpellbook spellbook) {
+ titleCache[blueprint.AssetGuid] = $"{spellbook.Name} - {spellbook.name}";
+ return $"{spellbook.Name} - {spellbook.name}";
+ }
+ name = formatter(uiDataProvider.Name);
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = formatter(blueprint.name);
+ } else if (Settings.showDisplayAndInternalNames) {
+ name += $" : {blueprint.name.Color(RGBA.darkgrey)}";
+ }
+ }
+ titleCache[blueprint.AssetGuid] = name;
+ return name;
+ } else if (blueprint is BlueprintItemEnchantment enchantment) {
+ string name;
+ var isEmpty = string.IsNullOrEmpty(enchantment.Name);
+ if (isEmpty) {
+ name = formatter(blueprint.name);
+ } else {
+ name = formatter(enchantment.Name);
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = formatter(blueprint.name);
+ } else if (Settings.showDisplayAndInternalNames) {
+ name += $" : {blueprint.name.Color(RGBA.darkgrey)}";
+ }
+ }
+ titleCache[blueprint.AssetGuid] = name;
+ return name;
+ }
+ titleCache[blueprint.AssetGuid] = formatter(blueprint.name);
+ return formatter(blueprint.name);
+ }
+ public static string GetSearchKey(SimpleBlueprint blueprint, bool forceDisplayInternalName = false) {
+ if (searchKeyCache.TryGetValue(blueprint.AssetGuid, out var ret) && (wasIncludeInternalNameForSearchKey == (Settings.showDisplayAndInternalNames || forceDisplayInternalName))) {
+ return ret;
+ } else {
+ wasIncludeInternalNameForSearchKey = Settings.showDisplayAndInternalNames || forceDisplayInternalName;
+ }
+ try {
+ if (blueprint is IUIDataProvider uiDataProvider) {
+ string name;
+ bool isEmpty = true;
+ try {
+ isEmpty = string.IsNullOrEmpty(uiDataProvider.Name);
+ } catch (Exception ex) {
+ Mod.Debug($"Error while getting name for {uiDataProvider}:\n{ex}");
+ }
+ if (isEmpty) {
+ name = blueprint.name;
+ } else {
+ if (uiDataProvider is BlueprintSpellbook spellbook) {
+ searchKeyCache[blueprint.AssetGuid] = $"{spellbook.Name} {spellbook.name} {spellbook.AssetGuid}";
+ return searchKeyCache[blueprint.AssetGuid];
+ }
+ name = uiDataProvider.Name;
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = blueprint.name;
+ } else if (Settings.showDisplayAndInternalNames || forceDisplayInternalName) {
+ name += $" : {blueprint.name}";
+ }
+ }
+ searchKeyCache[blueprint.AssetGuid] = name.StripHTML() + $" {blueprint.AssetGuid}";
+ return searchKeyCache[blueprint.AssetGuid];
+ } else if (blueprint is BlueprintItemEnchantment enchantment) {
+ string name;
+ var isEmpty = string.IsNullOrEmpty(enchantment.Name);
+ if (isEmpty) {
+ name = blueprint.name;
+ } else {
+ name = enchantment.Name;
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = blueprint.name;
+ } else if (Settings.showDisplayAndInternalNames) {
+ name += $" : {blueprint.name}";
+ }
+ }
+ searchKeyCache[blueprint.AssetGuid] = name.StripHTML() + $" {blueprint.AssetGuid}";
+ return searchKeyCache[blueprint.AssetGuid];
+ }
+ searchKeyCache[blueprint.AssetGuid] = blueprint.name.StripHTML() + $" {blueprint.AssetGuid}";
+ return searchKeyCache[blueprint.AssetGuid];
+ } catch (Exception ex) {
+ Mod.Debug(ex.ToString());
+ Mod.Debug($"-------{blueprint}-----{blueprint.AssetGuid}");
+ return "";
+ }
+ }
+ public static string GetSortKey(SimpleBlueprint blueprint) {
+ if (sortKeyCache.TryGetValue(blueprint.AssetGuid, out var ret) && (wasIncludeInternalNameForSortKey == Settings.showDisplayAndInternalNames)) {
+ return ret;
+ } else {
+ wasIncludeInternalNameForSortKey = Settings.showDisplayAndInternalNames;
+ }
+ try {
+ if (blueprint is IUIDataProvider uiDataProvider) {
+ string name;
+ bool isEmpty = true;
+ try {
+ isEmpty = string.IsNullOrEmpty(uiDataProvider.Name);
+ } catch (Exception ex) {
+ Mod.Debug($"Error while getting name for {uiDataProvider}:\n{ex}");
+ }
+ if (isEmpty) {
+ name = blueprint.name;
+ } else {
+ if (blueprint is BlueprintSpellbook spellbook) {
+ sortKeyCache[blueprint.AssetGuid] = $"{spellbook.Name} - {spellbook.name}";
+ return $"{spellbook.Name} - {spellbook.name}";
+ }
+ name = uiDataProvider.Name;
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = blueprint.name;
+ } else if (Settings.showDisplayAndInternalNames) {
+ name += blueprint.name;
+ }
+ }
+ sortKeyCache[blueprint.AssetGuid] = name;
+ return name;
+ } else if (blueprint is BlueprintItemEnchantment enchantment) {
+ string name;
+ var isEmpty = string.IsNullOrEmpty(enchantment.Name);
+ if (isEmpty) {
+ name = blueprint.name;
+ } else {
+ name = enchantment.Name;
+ if (name == "" || name.StartsWith("[unknown key: ")) {
+ name = blueprint.name;
+ } else if (Settings.showDisplayAndInternalNames) {
+ name += blueprint.name;
+ }
+ }
+ sortKeyCache[blueprint.AssetGuid] = name;
+ return name;
+ }
+ sortKeyCache[blueprint.AssetGuid] = blueprint.name;
+ return blueprint.name;
+ } catch (Exception ex) {
+ Mod.Debug(ex.ToString());
+ Mod.Debug($"-------{blueprint}-----{blueprint.AssetGuid}");
+ return "";
+ }
+ }
+ private static Dictionary, string)>> PropertyAccessors = new();
+ private static Dictionary TypeNamesCache = new();
+ public static void CacheTypeProperties(Type type) {
+ var accessors = new List<(Func, string)>();
+ // When get_IsContinuous is called, this will cause a chain rection which crashes the game...
+ foreach (var prop in type.GetProperties(AccessTools.allDeclared).Where(p => p.Name.StartsWith("Is") && p.PropertyType == typeof(bool) && !p.Name.StartsWith("IsContinuous"))) {
+ var mi = prop.GetGetMethod(true);
+ if (mi == null) continue;
+ if (mi.IsStatic) {
+ Func staticDelegate = (Func)Delegate.CreateDelegate(typeof(Func), mi);
+ accessors.Add((bp => staticDelegate(), prop.Name));
+ } else {
+ var parameter = Expression.Parameter(typeof(SimpleBlueprint), "bp");
+ var propertyAccess = Expression.Property(Expression.Convert(parameter, type), prop);
+ var lambda = Expression.Lambda>(propertyAccess, parameter);
+ Func compiled = lambda.Compile();
+ accessors.Add((compiled, prop.Name));
+ }
+ }
+
+ PropertyAccessors[type] = accessors;
+ }
+ public static IEnumerable Attributes(this SimpleBlueprint bp) {
+ if (BadList.Contains(bp.AssetGuid)) return Enumerable.Empty();
+ if (!PropertyAccessors.TryGetValue(bp.GetType(), out var accessors)) {
+ CacheTypeProperties(bp.GetType());
+ accessors = PropertyAccessors[bp.GetType()];
+ }
+
+ List modifiers = new List();
+ foreach (var accessor in accessors) {
+ try {
+ if (accessor.Item1(bp)) {
+ modifiers.Add(accessor.Item2);
+ }
+ } catch (Exception e) {
+ Mod.Warn($"Error accessing property on {bp.name}: {e.Message}");
+ BadList.Add(bp.AssetGuid);
+ break;
+ }
+ }
+ return modifiers;
+ }
+ private static List DefaultCollationNames(this SimpleBlueprint bp, string[] extras) {
+ _cachedCollationNames.TryGetValue(bp, out var names);
+ if (names == null) {
+ var namesSet = new HashSet();
+ string typeName;
+ var type = bp.GetType();
+ if (!TypeNamesCache.TryGetValue(type, out typeName)) {
+ typeName = type.Name;
+ typeName = typeName.Replace("Blueprint", "");
+
+ TypeNamesCache[type] = typeName;
+ }
+ namesSet.Add(typeName);
+
+ foreach (var attribute in bp.Attributes()) {
+ namesSet.Add(attribute.Orange());
+ }
+ names = namesSet.ToList();
+ _cachedCollationNames.Add(bp, names);
+ }
+
+ return [.. names, .. extras];
+ }
+ public static List CollationNames(this SimpleBlueprint bp, params string[] extras) => DefaultCollationNames(bp, extras);
+ [Obsolete]
+ public static List CollationNames(this BlueprintCharacterClass bp, params string[] extras) {
+ var names = DefaultCollationNames(bp, extras);
+ if (bp.IsArcaneCaster) names.Add("Arcane");
+ if (bp.IsDivineCaster) names.Add("Divine");
+ if (bp.IsMythic) names.Add("Mythic");
+ return names;
+ }
+ public static List CollationNames(this BlueprintSpellbook bp, params string[] extras) {
+ var names = DefaultCollationNames(bp, extras);
+ if (bp.CharacterClass.IsDivineCaster) names.Add("Divine");
+ AddOrUpdateCachedNames(bp, names);
+ return names;
+ }
+ public static List CollationNames(this BlueprintBuff bp, params string[] extras) {
+ var names = DefaultCollationNames(bp, extras);
+ if (bp.Ranks > 0) names.Add($"{bp.Ranks} Ranks");
+
+ AddOrUpdateCachedNames(bp, names);
+ return names;
+ }
+ public static List CollationNames(this BlueprintArea bp, params string[] extras) {
+ var names = DefaultCollationNames(bp, extras);
+ var typeName = bp.GetType().Name.Replace("Blueprint", "");
+ if (typeName == "Area") names.Add($"Area CR{bp.m_CR}");
+ AddOrUpdateCachedNames(bp, names);
+ return names;
+ }
+ public static List CollationNames(this BlueprintEtude bp, params string[] extras) {
+ var names = DefaultCollationNames(bp, extras);
+ //foreach (var item in bp.ActivationCondition) {
+ // names.Add(item.name.yellow());
+ //}
+ //names.Add(bp.ValidationStatus.ToString().yellow());
+ //if (bp.HasParent) names.Add($"P:".yellow() + bp.Parent.NameSafe());
+ //foreach (var sibling in bp.StartsWith) {
+ // names.Add($"W:".yellow() + bp.Parent.NameSafe());
+ //}
+ //if (bp.HasLinkedAreaPart) names.Add($"area {bp.LinkedAreaPart.name}".yellow());
+ //foreach (var condition in bp.ActivationCondition?.Conditions)
+ // names.Add(condition.GetCaption().yellow());
+ AddOrUpdateCachedNames(bp, names);
+ return names;
+ }
+ public static string[] CaptionNames(this SimpleBlueprint bp) => bp.m_AllElements?.OfType()?.Select(e => e.GetCaption() ?? "")?.ToArray() ?? new string[] { };
+ public static List CaptionCollationNames(this SimpleBlueprint bp) => bp.CollationNames(bp.CaptionNames());
+
+ public static readonly HashSet badBP = new() { "b60252a8ae028ba498340199f48ead67", "fb379e61500421143b52c739823b4082" };
+ public static string GetDescription(this SimpleBlueprint bp)
+ // borrowed shamelessly and enhanced from Bag of Tricks https://www.nexusmods.com/pathfinderkingmaker/mods/26, which is under the MIT License
+ {
+ try {
+ // avoid exceptions on known broken items
+ var guid = bp.AssetGuid;
+ if (descriptionCache.TryGetValue(guid, out var desc)) {
+ return desc;
+ }
+ if (badBP.Contains(guid)) return null;
+ var associatedBlueprint = bp as IUIDataProvider;
+ desc = associatedBlueprint?.Description?.StripHTML();
+ descriptionCache[guid] = desc;
+ return desc;
+
+ } catch (Exception e) {
+ Mod.Debug(e.ToString());
+#if DEBUG
+ return "ERROR".Red().Bold() + $": caught exception {e}";
+#else
+ return "";
+#endif
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ToyBox/classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs
similarity index 72%
rename from ToyBox/classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs
rename to ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs
index aab098680..add98f138 100644
--- a/ToyBox/classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs
+++ b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintExtensionsQuest.cs
@@ -1,59 +1,64 @@
// Copyright < 2021 > Narria (github user Cabarius) - License: MIT
using HarmonyLib;
+using Kingmaker;
using Kingmaker.AreaLogic.Etudes;
+using Kingmaker.AreaLogic.QuestSystem;
using Kingmaker.Blueprints;
using Kingmaker.Blueprints.Area;
using Kingmaker.Blueprints.Classes;
-using Kingmaker.Blueprints.Classes.Selection;
-using Kingmaker.Blueprints.Classes.Spells;
using Kingmaker.Blueprints.Facts;
using Kingmaker.Blueprints.Items;
using Kingmaker.Blueprints.Items.Ecnchantments;
-using Kingmaker.Craft;
+using Kingmaker.Designers.EventConditionActionSystem.Actions;
+using Kingmaker.Designers.EventConditionActionSystem.Conditions;
+using Kingmaker.DialogSystem.Blueprints;
using Kingmaker.ElementsSystem;
+using Kingmaker.EntitySystem;
using Kingmaker.EntitySystem.Entities;
using Kingmaker.UI;
using Kingmaker.UnitLogic;
using Kingmaker.UnitLogic.Buffs.Blueprints;
+using Kingmaker.UnitLogic.Interaction;
using Kingmaker.UnitLogic.Mechanics;
+using Kingmaker.UnitLogic.Parts;
using Kingmaker.Utility;
+using Kingmaker.View.MapObjects;
using ModKit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
-using Kingmaker.AreaLogic.QuestSystem;
-using Kingmaker.Designers.EventConditionActionSystem.Conditions;
-using Kingmaker.UnitLogic.Parts;
using static Kingmaker.UnitLogic.Interaction.SpawnerInteractionPart;
-using Kingmaker.UnitLogic.Interaction;
using static ToyBox.BlueprintExtensions;
-using Kingmaker.Designers.EventConditionActionSystem.Actions;
-using Kingmaker;
-using Kingmaker.EntitySystem;
-using Kingmaker.View.MapObjects;
namespace ToyBox {
public static partial class BlueprintExtensions {
public class IntrestingnessEntry {
- public UnitEntityData unit { get; set; }
+ public BaseUnitEntity unit { get; set; }
public object source { get; set; }
public ConditionsChecker checker { get; set; }
- public List elements { get; set; }
- public bool HasConditins => checker?.Conditions.Length > 0;
+ public List? elements { get; set; }
+ public bool HasConditions => checker?.Conditions.Length > 0;
public bool HasElements => elements?.Count > 0;
- public IntrestingnessEntry(UnitEntityData unit, object source, ConditionsChecker checker, List elements = null) {
+ public IntrestingnessEntry(BaseUnitEntity unit, object source, ConditionsChecker checker, List? elements = null) {
this.unit = unit;
this.source = source;
this.checker = checker;
this.elements = elements;
}
}
- public static bool IsActive(this IntrestingnessEntry entry) =>
+ public static bool IsActive(this IntrestingnessEntry entry) =>
(entry.checker?.IsActive() ?? false)
- || (entry?.elements.Any(element => element.IsActive()) ?? false)
- || (entry.elements?.Count > 0 && entry.source is ActionsHolder) // Kludge until we get more clever about analyzing dialog state. This lets Lathimas show up as active
+ || (entry?.elements.Any(element => {
+ try {
+ return element.IsActive();
+ } catch (Exception ex) {
+ Mod.Debug(ex.ToString());
+ return false;
+ }
+ }) ?? false)
+ || (entry?.elements?.Count > 0 && entry.source is ActionsHolder) // Kludge until we get more clever about analyzing dialog state. This lets Lathimas show up as active
;
public static bool IsActive(this Element element) => element switch {
Conditional conditional => conditional.ConditionsChecker.Check(),
@@ -62,8 +67,8 @@ public static bool IsActive(this IntrestingnessEntry entry) =>
};
public static bool IsActive(this ConditionsChecker checker) => checker.Conditions.Any(c => c.CheckCondition());
public static string CaptionString(this Condition condition) =>
- $"{condition.GetCaption().orange()} -> {(condition.CheckCondition() ? "True".green() : "False".yellow())}";
- public static string CaptionString(this Element element) => $"{element.GetCaption().orange()}";
+ $"{condition.GetCaption().Orange()} -> {(condition.CheckCondition() ? "True".Green() : "False".Yellow())}";
+ public static string CaptionString(this Element element) => $"{element.GetCaption().Orange()}";
public static bool IsQuestRelated(this Element element) => element is GiveObjective
|| element is SetObjectiveStatus
@@ -75,17 +80,35 @@ public static bool IsQuestRelated(this Element element) => element is GiveObject
|| element is ItemsEnough
|| element is Conditional
;
- public static int InterestingnessCoefficent(this UnitEntityData unit) => unit.GetUnitInteractionConditions().Count(entry => entry.IsActive());
-
- public static IEnumerable GetUnitInteractionConditions(this UnitEntityData unit) {
- var spawnInterations = unit.Parts.Parts
+ public static int InterestingnessCoefficent(this MechanicEntity entity)
+ => entity is BaseUnitEntity unit ? unit.InterestingnessCoefficent() : 0;
+ public static int InterestingnessCoefficent(this BaseUnitEntity unit) => unit.GetUnitInteractionConditions().Count(entry => {
+ try {
+ return entry.IsActive();
+ } catch (Exception ex) {
+ Mod.Debug(ex.ToString());
+ return false;
+ }
+ });
+ public static List GetDialog(this BaseUnitEntity unit) {
+ var dialogs = unit.Parts.m_Parts
+ .OfType()
+ .SelectMany(p => p.m_Interactions)
+ .OfType()
+ .Select(w => w.Source)
+ .OfType()
+ .Select(sid => sid.Dialog).ToList();
+ return dialogs;
+ }
+ public static IEnumerable GetUnitInteractionConditions(this BaseUnitEntity unit) {
+ var spawnInterations = unit.Parts.m_Parts
.OfType()
.SelectMany(p => p.m_Interactions)
.OfType()
.Select(w => w.Source);
var result = new HashSet();
var elements = new HashSet();
-
+
// dialog
var dialogInteractions = spawnInterations.OfType().ToList();
// dialog interation conditions
@@ -108,7 +131,7 @@ public static IEnumerable GetUnitInteractionConditions(this
.Where(cueRef => cueRef.Get() != null)
.Select(cueRef => new IntrestingnessEntry(unit, cueRef.Get(), cueRef.Get().Conditions)));
result.UnionWith(dialogCueConditions.ToHashSet());
-
+
// actions
var actionInteractions = spawnInterations.OfType();
// action interaction conditions
@@ -118,15 +141,17 @@ public static IEnumerable GetUnitInteractionConditions(this
result.UnionWith(actionInteractionConditions.ToHashSet());
// action conditions
var actionConditions = actionInteractions
- .Where(ai => ai.Actions?.Get() != null)
- .SelectMany(ai => ai.Actions.Get().Actions.Actions
+ .SelectMany(ai => ai.ActionHolders)
+ .Where(ai => ai?.Get() != null)
+ .SelectMany(ai => ai.Get().Actions.Actions
.Where(a => a is Conditional)
- .Select(a => new IntrestingnessEntry(unit, ai.Actions.Get(), (a as Conditional).ConditionsChecker)));
+ .Select(a => new IntrestingnessEntry(unit, ai.Get(), (a as Conditional).ConditionsChecker)));
result.Union(actionConditions.ToHashSet());
// action elements
var actionElements = actionInteractions
- .Where(ai => ai.Actions?.Get() != null)
- .Select(ai => new IntrestingnessEntry(unit, ai.Actions.Get(), null, ai.Actions.Get().ElementsArray.Where(e => e.IsQuestRelated()).ToList()));
+ .SelectMany(ai => ai.ActionHolders)
+ .Where(ai => ai?.Get() != null)
+ .Select(ai => new IntrestingnessEntry(unit, ai.Get(), null, ai.Get().ElementsArray.Where(e => e.IsQuestRelated()).ToList()));
elements.UnionWith(actionElements.ToHashSet());
foreach (var entry in elements) {
//Mod.Debug($"checking {entry}");
@@ -146,11 +171,11 @@ public static IEnumerable GetUnitInteractionConditions(this
return result;
}
public static void RevealInterestingNPCs() {
- if (Game.Instance?.State?.Units is { } unitsPool) {
+ if (Shodan.AllUnits
+ is { } unitsPool) {
var inerestingUnits = unitsPool.Where(u => u.InterestingnessCoefficent() > 0);
foreach (var unit in inerestingUnits) {
Mod.Debug($"Revealing {unit.CharacterName}");
- unit.SetIsRevealedSilent(true);
}
}
}
diff --git a/ToyBox/Classes/Infrastructure/Blueprints/BlueprintIdCache.cs b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintIdCache.cs
new file mode 100644
index 000000000..10d85322c
--- /dev/null
+++ b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintIdCache.cs
@@ -0,0 +1,202 @@
+using Kingmaker.Blueprints;
+using Kingmaker;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Kingmaker.Modding;
+using Kingmaker.AreaLogic.Etudes;
+using Kingmaker.Blueprints.Area;
+using Kingmaker.Blueprints.Items.Ecnchantments;
+using Kingmaker.UnitLogic.Buffs.Blueprints;
+using Kingmaker.UnitLogic.Abilities.Blueprints;
+using Kingmaker.AI.Blueprints;
+using System.IO;
+using System.Runtime.Serialization;
+using Kingmaker.Blueprints.Facts;
+using Kingmaker.GameInfo;
+using Kingmaker.Globalmap.Blueprints.SystemMap;
+using Kingmaker.Globalmap.Blueprints.Colonization;
+using Kingmaker.Globalmap.Blueprints;
+using Kingmaker.Visual.Sound;
+using Kingmaker.Blueprints.Items;
+using Kingmaker.Blueprints.Items.Weapons;
+using Kingmaker.Blueprints.Items.Armors;
+using Kingmaker.Blueprints.Items.Equipment;
+
+namespace ToyBox.classes.Infrastructure.Blueprints {
+ public class BlueprintIdCache {
+ public string CachedGameVersion = "";
+ public HashSet<(string, string)> UmmList = new();
+ public HashSet<(string, string)> OmmList = new();
+ public Dictionary> IdsByType = new();
+
+ public static HashSet CachedIdTypes = new() {
+ typeof(BlueprintItem), typeof(BlueprintItemWeapon), typeof(BlueprintItemArmor),
+ typeof(BlueprintEtude), typeof(BlueprintArea), typeof(BlueprintItemEnchantment),
+ typeof(BlueprintBuff), typeof(BlueprintPortrait), typeof(BlueprintSpellbook),
+ typeof(BlueprintAbility), typeof(BlueprintAreaEnterPoint), typeof(BlueprintUnit),
+ typeof(BlueprintBrain), typeof(BlueprintFeature), typeof(BlueprintUnitFact),
+ typeof(BlueprintPlanet), typeof(BlueprintColony), typeof(BlueprintStarSystemMap),
+ typeof(BlueprintColonyTrait), typeof(BlueprintResource), typeof(BlueprintColonyEventResult),
+ typeof(BlueprintUnitAsksList), typeof(BlueprintAbilityFXSettings), typeof(BlueprintAreaPreset),
+ typeof(BlueprintItemMechadendrite)
+ };
+
+ private static BlueprintIdCache _instance;
+ public static BlueprintIdCache Instance {
+ get {
+ if (_instance == null) {
+ Load();
+ }
+ return _instance;
+ }
+ }
+
+ private static bool? _needsCacheRebuilt = null;
+ public static bool NeedsCacheRebuilt {
+ get {
+ if (_needsCacheRebuilt.HasValue) return _needsCacheRebuilt.Value && !isRebuilding;
+
+ bool gameVersionChanged = GameVersion.GetVersion() != Instance.CachedGameVersion;
+
+ var ummSet = Instance.UmmList.ToHashSet();
+ bool ummModsChanged = !(ummSet.Count == UnityModManagerNet.UnityModManager.ModEntries.Count);
+ if (!ummModsChanged) {
+ foreach (var modEntry in UnityModManagerNet.UnityModManager.ModEntries) {
+ if (!ummSet.Contains(new(modEntry.Info.Id, modEntry.Info.Version))) {
+ ummModsChanged = true;
+ break;
+ }
+ }
+ }
+
+ var ommSet = Instance.OmmList.ToHashSet();
+ bool ommModsChanged = !(ommSet.Count == OwlcatModificationsManager.s_Instance.AppliedModifications.Count());
+ if (!ommModsChanged) {
+ foreach (var modEntry in OwlcatModificationsManager.s_Instance.AppliedModifications) {
+ if (!ommSet.Contains(new(modEntry.Manifest.UniqueName, modEntry.Manifest.Version))) {
+ ommModsChanged = true;
+ break;
+ }
+ }
+ }
+ ModKit.Mod.Log($"Test for BPId Cache constincy: Game Version Changed: {gameVersionChanged}; UMM Mod Changed: {ummModsChanged}; OMM Mod Changed: {ommModsChanged}");
+ _needsCacheRebuilt = gameVersionChanged || ummModsChanged || ommModsChanged;
+ return _needsCacheRebuilt.Value && !isRebuilding;
+ }
+ }
+
+ public static bool isRebuilding = false;
+ internal static void RebuildCache(List blueprints) {
+ if (!Main.Settings.toggleUseBPIdCache) return;
+ ModKit.Mod.Log("Starting to build BPId Cache");
+ isRebuilding = true;
+ try {
+ // Header
+ Instance.CachedGameVersion = BlueprintLoader.GameVersion;
+
+ Instance.UmmList.Clear();
+ foreach (var modEntry in UnityModManagerNet.UnityModManager.ModEntries) {
+ Instance.UmmList.Add(new(modEntry.Info.Id, modEntry.Info.Version));
+ }
+
+ Instance.OmmList.Clear();
+ foreach (var modEntry in OwlcatModificationsManager.s_Instance.AppliedModifications) {
+ Instance.OmmList.Add(new(modEntry.Manifest.UniqueName, modEntry.Manifest.Version));
+ }
+
+ //Ids
+ Instance.IdsByType.Clear();
+ foreach (var type in CachedIdTypes) {
+ HashSet idsForType = new();
+ foreach (var bp in blueprints) {
+ if (type.IsInstanceOfType(bp)) {
+ idsForType.Add(bp.AssetGuid);
+ }
+ }
+ Instance.IdsByType[type] = idsForType;
+ }
+
+ Instance.Save();
+ } catch (Exception ex) {
+ ModKit.Mod.Error(ex.ToString());
+ }
+ _needsCacheRebuilt = false;
+ isRebuilding = false;
+ ModKit.Mod.Log("Finished rebuilding BPId Cache");
+ }
+ internal void Save() {
+ using var stream = new FileStream(EnsureFilePath(), FileMode.Create, FileAccess.Write);
+ using var writer = new BinaryWriter(stream, Encoding.UTF8);
+
+ writer.Write(CachedGameVersion);
+
+ writer.Write(UmmList.Count);
+ foreach (var (item1, item2) in UmmList) {
+ writer.Write(item1);
+ writer.Write(item2);
+ }
+
+ writer.Write(OmmList.Count);
+ foreach (var (item1, item2) in OmmList) {
+ writer.Write(item1);
+ writer.Write(item2);
+ }
+
+ writer.Write(IdsByType.Count);
+ foreach (var kvp in IdsByType) {
+ writer.Write(kvp.Key.AssemblyQualifiedName);
+ writer.Write(kvp.Value.Count);
+ foreach (var guid in kvp.Value) {
+ writer.Write(guid.ToString());
+ }
+ }
+ }
+ private static void Load() {
+ try {
+ using var stream = new FileStream(EnsureFilePath(), FileMode.Open, FileAccess.Read);
+ using var reader = new BinaryReader(stream, Encoding.UTF8);
+
+ BlueprintIdCache result = new();
+ result.CachedGameVersion = reader.ReadString();
+
+ int ummCount = reader.ReadInt32();
+ for (int i = 0; i < ummCount; i++) {
+ result.UmmList.Add((reader.ReadString(), reader.ReadString()));
+ }
+
+ int ommCount = reader.ReadInt32();
+ for (int i = 0; i < ommCount; i++) {
+ result.OmmList.Add((reader.ReadString(), reader.ReadString()));
+ }
+
+ int dictCount = reader.ReadInt32();
+ for (int i = 0; i < dictCount; i++) {
+
+ string typeName = reader.ReadString();
+ Type type = Type.GetType(typeName);
+ if (type == null) ModKit.Mod.Error(new SerializationException($"Type {typeName} not found.").ToString());
+
+ int listCount = reader.ReadInt32();
+ var guidList = new HashSet();
+ for (int j = 0; j < listCount; j++) {
+ guidList.Add(reader.ReadString());
+ }
+ result.IdsByType[type] = guidList;
+ }
+ _instance = result;
+ } catch (FileNotFoundException) {
+ _instance = new();
+ _instance.Save();
+ }
+ }
+
+ private static string EnsureFilePath() {
+ var userConfigFolder = Path.Combine(Main.ModEntry.Path, "UserSettings");
+ Directory.CreateDirectory(userConfigFolder);
+ return Path.Combine(userConfigFolder, "BlueprintIdCache.bin");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ToyBox/Classes/Infrastructure/Blueprints/BlueprintLoader.cs b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintLoader.cs
new file mode 100644
index 000000000..c2d1242c0
--- /dev/null
+++ b/ToyBox/Classes/Infrastructure/Blueprints/BlueprintLoader.cs
@@ -0,0 +1,398 @@
+// Copyright < 2021 > Narria(github user Cabarius) - License: MIT
+using HarmonyLib;
+using Kingmaker;
+using Kingmaker.Blueprints;
+using Kingmaker.Blueprints.JsonSystem;
+using Kingmaker.Blueprints.JsonSystem.BinaryFormat;
+using Kingmaker.Blueprints.JsonSystem.Converters;
+using Kingmaker.Localization;
+using Kingmaker.Modding;
+using ModKit;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Owlcat.Runtime.Core.Utility;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using ToyBox.classes.Infrastructure.Blueprints;
+
+namespace ToyBox {
+ public class SharedStringAssetPool : MonoSingleton {
+ public const int Size = 100;
+ private readonly ConcurrentQueue m_Pool = new();
+ private SemaphoreSlim m_Available;
+ private void Awake() {
+ m_Available = new SemaphoreSlim(0, int.MaxValue);
+ for (var i = 0; i < Size; i++) {
+ var inst = UnityEngine.ScriptableObject.CreateInstance();
+ m_Pool.Enqueue(inst);
+ m_Available.Release();
+ }
+ }
+ private void OnDestroy() {
+ while (m_Pool.TryDequeue(out var result)) {
+ Destroy(result);
+ }
+ m_Available.Dispose();
+ }
+ private void Update() {
+ var toAdd = Size - m_Pool.Count;
+ for (var i = 0; i < toAdd; i++) {
+ var inst = UnityEngine.ScriptableObject.CreateInstance();
+ m_Pool.Enqueue(inst);
+ m_Available.Release();
+ }
+ }
+ public SharedStringAsset Request() {
+ if (UnityEngine.Object.CurrentThreadIsMainThread()) {
+ return UnityEngine.ScriptableObject.CreateInstance();
+ } else {
+ m_Available.Wait();
+ if (m_Pool.TryDequeue(out var requested)) {
+ return requested;
+ } else {
+ Mod.Log($"[Critical] SharedStringAssetPool had no instance in pool despite signalling availability! {new StackTrace()}");
+ throw new InvalidOperationException($"SharedStringAssetPool had no instance in pool");
+ }
+ }
+ }
+ }
+ public class BlueprintLoader {
+ public static string GameVersion;
+ public delegate void LoadBlueprintsCallback(List blueprints);
+ private List blueprints;
+ private Dictionary> bpsByType = new();
+ private HashSet bpsToAdd = new();
+ internal bool CanStart = false;
+ public float progress = 0;
+ private static BlueprintLoader loader;
+ public static BlueprintLoader Shared {
+ get {
+ if (GameVersion.IsNullOrEmpty()) {
+ GameVersion = Kingmaker.GameInfo.GameVersion.GetVersion();
+ }
+ loader ??= new();
+ return loader;
+ }
+ }
+ internal readonly HashSet BadBlueprints = new() { "ce0842546b73aa34b8fcf40a970ede68", "2e3280bf21ec832418f51bee5136ec7a",
+ "b60252a8ae028ba498340199f48ead67", "fb379e61500421143b52c739823b4082", "5d2b9742ce82457a9ae7209dce770071" };
+ private void Load(LoadBlueprintsCallback callback, ISet toLoad = null) {
+ lock (loader) {
+ if (IsLoading || (!CanStart && ResourcesLibrary.BlueprintsCache.m_PackFile == null) || blueprints != null) return;
+ loader.Init(callback, toLoad);
+ }
+ }
+ public bool IsLoading => loader.IsRunning;
+ public List GetBlueprints() {
+ if (blueprints == null) {
+ lock (loader) {
+ if (Shared.IsLoading) {
+ return null;
+ } else {
+ Mod.Debug($"Calling BlueprintLoader.Load");
+ Shared.Load((bps) => {
+ lock (bpsToAdd) {
+ bps.AddRange(bpsToAdd);
+ bpsToAdd.Clear();
+ }
+ blueprints = bps;
+ if (BlueprintIdCache.NeedsCacheRebuilt) BlueprintIdCache.RebuildCache(blueprints);
+ bpsByType.Clear();
+ }, toLoad);
+ return null;
+ }
+ }
+ }
+ lock (bpsToAdd) {
+ if (bpsToAdd.Count > 0) {
+ blueprints.AddRange(bpsToAdd);
+ bpsToAdd.Clear();
+ }
+ }
+ return blueprints;
+ }
+ public static IEnumerable BlueprintsOfType(Type type) {
+ return (IEnumerable)AccessTools.Method(typeof(BlueprintLoader), nameof(GetBlueprintsOfType)).MakeGenericMethod(type).Invoke(Shared, null);
+ }
+ public IEnumerable GetBlueprintsOfType() where BPType : SimpleBlueprint {
+ if (blueprints == null) {
+ if (Main.Settings.toggleUseBPIdCache && !BlueprintIdCache.NeedsCacheRebuilt) {
+ if (bpsByType.TryGetValue(typeof(BPType), out var bps)) {
+ return bps.Cast();
+ } else if (BlueprintIdCache.Instance.IdsByType.TryGetValue(typeof(BPType), out var ids)) {
+ if (!Shared.IsLoading) {
+ Load(bps => {
+ IEnumerable toAdd;
+ toAdd = bpsToAdd.OfType() ?? new List