diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d32842 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,223 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = false +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = false + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true +csharp_style_prefer_parameter_null_checking = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = false +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = false +csharp_style_prefer_tuple_swap = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_embedded_statements_on_same_line_experimental = false + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = none +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml new file mode 100644 index 0000000..c59f025 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + - type: checkboxes + id: before + attributes: + label: Before You Report + description: Following these steps first gives us the best chance of fixing your problem. + options: + - label: My mods and Everest install are up to date. + required: false + - label: I have recreated the bug with a minimum number of mods installed. + required: false + + - type: input + id: platform + attributes: + label: What platform are you playing on? + description: e.g. Windows 10 FNA, Mac, Linux, etc. + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Bug Description + description: What happened? And what did you expect to happen? + value: "Please give a clear and concise description of the bug." + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + description: How do we trigger this bug ourselves? + value: "Attach screenshots, video, a map, etc. as needed." + validations: + required: true + + - type: textarea + id: log + attributes: + label: Log Output + description: Please copy and paste any relevant log output. + render: shell diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml new file mode 100644 index 0000000..299a4f9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml @@ -0,0 +1,22 @@ +name: Feature Request +description: Suggest a feature for the mod +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: textarea + id: request + attributes: + label: Request Description + description: Please describe your request. + value: "Attach screenshots, concept art, etc. as needed." + validations: + required: true + + - type: input + id: deadline + attributes: + label: Target Date + description: If you have a time requirement, e.g. for a contest, list it here. + placeholder: Filling this out does not guarantee we can fulfill the request. + validations: + required: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9ea2370 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,42 @@ +name: Build + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Download Everest stripped lib + uses: robinraju/release-downloader@v1.5 + with: + repository: EverestAPI/Everest + latest: true + fileName: lib-stripped.zip + + - name: Extract lib-stripped.zip + run: unzip lib-stripped.zip + + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 5.0.x + + - name: Install dependencies + run: dotnet restore + + - name: Build with .NET Core + run: dotnet build --configuration Debug --no-restore + env: + CELESTEPREFIX: ${{ github.workspace }}/lib-stripped + + - name: Upload build + uses: actions/upload-artifact@v3 + with: + name: bin + path: Code/bin/Debug/net452 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..75c875a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release + +on: + workflow_dispatch: + push: + branches: [ dev ] + paths: + - 'everest.yaml' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Download Everest stripped lib + uses: robinraju/release-downloader@v1.5 + with: + repository: EverestAPI/Everest + latest: true + fileName: lib-stripped.zip + + - name: Extract lib-stripped.zip + run: unzip lib-stripped.zip + + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 5.0.x + + - name: Install dependencies + run: dotnet restore + + - name: Build with .NET Core + run: dotnet build --configuration Debug --no-restore + env: + CELESTEPREFIX: ${{ github.workspace }}/lib-stripped + + - name: Upload ZIP + uses: actions/upload-artifact@v3 + with: + name: IsaGrabBag + path: | + Ahorn + Code/bin + Effects + Graphics + everest.yaml + !Code/bin/Debug \ No newline at end of file diff --git a/.gitignore b/.gitignore index 12a2548..fefa62b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ +[Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ @@ -42,9 +43,10 @@ Generated\ Files/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ @@ -125,9 +127,6 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* @@ -185,6 +184,8 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. @@ -260,7 +261,9 @@ ServiceFabricBackup/ *.bim.layout *.bim_*.settings *.rptproj.rsuser -*- Backup*.rdl +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -343,91 +346,9 @@ healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# Mac bundle stuff -*.dmg -*.app - -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ # JetBrains Rider -.idea/ -*.sln.iml +.idea -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..92aba1b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "IsaGrabBag.sln" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index e6e24db..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/CelesteMod.sln", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/CelesteMod.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/Ahorn/entities/arrowblock.jl b/Ahorn/entities/arrowblock.jl new file mode 100644 index 0000000..7a8ceb9 --- /dev/null +++ b/Ahorn/entities/arrowblock.jl @@ -0,0 +1,79 @@ +module IsaGrabBagArrowBlock + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/arrowBlock" IsaArrowBlock(x::Integer, y::Integer, distance::Integer=16, inverted::Bool=false, movementRestriction::String="no_limit") + +const placements = Ahorn.PlacementDict( + "Friendly Kevin (IsaGrabBag)" => Ahorn.EntityPlacement( + IsaArrowBlock, + "rectangle" + ) +) + +Ahorn.minimumSize(entity::IsaArrowBlock) = 24, 24 +Ahorn.resizable(entity::IsaArrowBlock) = true, true + +const axes = String[ + "no_limit", + "horizontal", + "vertical", + "cardinal", + "diagonal", +] + +Ahorn.editingOptions(entity::IsaArrowBlock) = Dict{String, Any}( + "movementRestriction" => axes +) + +function Ahorn.selection(entity::IsaArrowBlock) + x, y = Ahorn.position(entity) + + width = Int(get(entity.data, "width", 8)) + height = Int(get(entity.data, "height", 8)) + + return Ahorn.Rectangle(x, y, width, height) +end + +const frameImage = Dict{String, String}( + "no_limit" => "isafriend/objects/arrowblock/block00", + "horizontal" => "isafriend/objects/arrowblock/block01", + "vertical" => "isafriend/objects/arrowblock/block02", + "cardinal" => "isafriend/objects/arrowblock/block03", + "diagonal" => "isafriend/objects/arrowblock/block04" +) +const kevinColor = (72, 59, 105) ./ 255 + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::IsaArrowBlock, room::Maple.Room) + + x, y = Ahorn.position(entity) + + width = Int(get(entity.data, "width", 32)) + height = Int(get(entity.data, "height", 32)) + + frame = frameImage[String(get(entity.data, "movementRestriction", "no_limit"))] + faceSprite = Ahorn.getSprite("isafriend/objects/arrowblock/idle_face", "Gameplay") + + tilesWidth = div(width, 8) + tilesHeight = div(height, 8) + + Ahorn.drawRectangle(ctx, 2, 2, width - 4, height - 4, kevinColor) + Ahorn.drawImage(ctx, faceSprite, div(width - faceSprite.width, 2), div(height - faceSprite.height, 2)) + + for i in 2:tilesWidth - 1 + Ahorn.drawImage(ctx, frame, (i - 1) * 8, 0, 8, 0, 8, 8) + Ahorn.drawImage(ctx, frame, (i - 1) * 8, height - 8, 8, 24, 8, 8) + end + + for i in 2:tilesHeight - 1 + Ahorn.drawImage(ctx, frame, 0, (i - 1) * 8, 0, 8, 8, 8) + Ahorn.drawImage(ctx, frame, width - 8, (i - 1) * 8, 24, 8, 8, 8) + end + + Ahorn.drawImage(ctx, frame, 0, 0, 0, 0, 8, 8) + Ahorn.drawImage(ctx, frame, width - 8, 0, 24, 0, 8, 8) + Ahorn.drawImage(ctx, frame, 0, height - 8, 0, 24, 8, 8) + Ahorn.drawImage(ctx, frame, width - 8, height - 8, 24, 24, 8, 8) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/arrowbubble.jl b/Ahorn/entities/arrowbubble.jl new file mode 100644 index 0000000..60acaba --- /dev/null +++ b/Ahorn/entities/arrowbubble.jl @@ -0,0 +1,63 @@ +module IsaGrabBagArrowBubble + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/arrowBubble" ArrowBubble(x::Integer, y::Integer, direction::String="down") + +const directions = String[ + "up", + "down", + "left", + "right" +] + +Ahorn.editingOptions(entity::ArrowBubble) = Dict{String, Any}( + "direction" => directions +) + +const placements = Ahorn.PlacementDict( + "Arrow Bubble (Down, IsaGrabBag)" => Ahorn.EntityPlacement( + ArrowBubble, + "rectangle", + Dict{String, Any}( + "direction" => "down" + ) + ), + "Arrow Bubble (Up, IsaGrabBag)" => Ahorn.EntityPlacement( + ArrowBubble, + "rectangle", + Dict{String, Any}( + "direction" => "up" + ) + ), + "Arrow Bubble (Left, IsaGrabBag)" => Ahorn.EntityPlacement( + ArrowBubble, + "rectangle", + Dict{String, Any}( + "direction" => "left" + ) + ), + "Arrow Bubble (Right, IsaGrabBag)" => Ahorn.EntityPlacement( + ArrowBubble, + "rectangle", + Dict{String, Any}( + "direction" => "right" + ) + ) +) + +function Ahorn.selection(entity::ArrowBubble) + x, y = Ahorn.position(entity) + + return Ahorn.getSpriteRectangle("isafriend/objects/booster/boosterdown00.png", x, y) + +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::ArrowBubble, room::Maple.Room) + + dir = String(get(entity.data, "direction", "down")) + + Ahorn.drawSprite(ctx, "isafriend/objects/booster/booster$(dir)00.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/baddyFollow.jl b/Ahorn/entities/baddyFollow.jl new file mode 100644 index 0000000..b27e782 --- /dev/null +++ b/Ahorn/entities/baddyFollow.jl @@ -0,0 +1,24 @@ +module IsaGrabBagBaddyFollow + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/baddyFollow" BadelineFriend(x::Integer, y::Integer, nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[]) + +const placements = Ahorn.PlacementDict( + "Badeline Friend (IsaGrabBag)" => Ahorn.EntityPlacement( + BadelineFriend, + "rectangle" + ) +) + +function Ahorn.selection(entity::BadelineFriend) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle("isafriend/baddyAhorn.png", x, y) + +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::BadelineFriend, room::Maple.Room) + Ahorn.drawSprite(ctx, "isafriend/baddyAhorn.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/cornerboost.jl b/Ahorn/entities/cornerboost.jl new file mode 100644 index 0000000..51ac990 --- /dev/null +++ b/Ahorn/entities/cornerboost.jl @@ -0,0 +1,37 @@ +module IsaGrabBagCornerBlock + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/cornerBlock" CornerBoostBlock(x::Integer, y::Integer, tiletype::String="1", useTileset::Bool=false) + +Ahorn.minimumSize(entity::CornerBoostBlock) = 8, 8 +Ahorn.resizable(entity::CornerBoostBlock) = true, true + +Ahorn.editingOptions(entity::CornerBoostBlock) = Dict{String, Any}( + "tiletype" => Ahorn.tiletypeEditingOptions() +) + +function Ahorn.selection(entity::CornerBoostBlock) + x, y = Ahorn.position(entity) + + width = Int(get(entity.data, "width", 8)) + height = Int(get(entity.data, "height", 8)) + + return Ahorn.Rectangle(x, y, width, height) +end + +function Ahorn.renderAbs(ctx::Ahorn.Cairo.CairoContext, entity::CornerBoostBlock, room::Maple.Room) + + if get(entity.data, "useTileset", false) + Ahorn.drawTileEntity(ctx, room, entity) + else + x, y = Ahorn.position(entity) + + width = Int(get(entity.data, "width", 32)) + height = Int(get(entity.data, "height", 32)) + + Ahorn.drawRectangle(ctx, x, y, width, height, (0.8, 0.8, 0.8, 1.0), (0.4, 0.4, 0.4, 1.0)) + end +end + +end \ No newline at end of file diff --git a/Ahorn/entities/dreamSpinnerFake.jl b/Ahorn/entities/dreamSpinnerFake.jl new file mode 100644 index 0000000..1617aee --- /dev/null +++ b/Ahorn/entities/dreamSpinnerFake.jl @@ -0,0 +1,22 @@ +module IsaGrabBagDreamSpinnerFake + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/dreamSpinFake" DreamSpinnerFake(x::Integer, y::Integer) + +const placements = Ahorn.PlacementDict( + "Dream Spinner Fake (IsaGrabBag)" => Ahorn.EntityPlacement( + DreamSpinnerFake + ) +) + +function Ahorn.selection(entity::DreamSpinnerFake) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle("isafriend/danger/crystal/fg_dreamspinner_fake.png", x, y) +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::DreamSpinnerFake, room::Maple.Room) + Ahorn.drawSprite(ctx, "isafriend/danger/crystal/fg_dreamspinner_fake.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/dreamspinner.jl b/Ahorn/entities/dreamspinner.jl new file mode 100644 index 0000000..583cc2c --- /dev/null +++ b/Ahorn/entities/dreamspinner.jl @@ -0,0 +1,29 @@ +module IsaGrabBagDreamSpinner + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/dreamSpinner" DreamSpinner(x::Integer, y::Integer, useOnce::Bool=false) + +const placements = Ahorn.PlacementDict( + "Dream Spinner (IsaGrabBag)" => Ahorn.EntityPlacement( + DreamSpinner + ), + "Dream Spinner (One Use) (IsaGrabBag)" => Ahorn.EntityPlacement( + DreamSpinner, + "rectangle", + Dict{String, Any}( + "useOnce" => true + ) + ) +) + +function Ahorn.selection(entity::DreamSpinner) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle("isafriend/danger/crystal/fg_dreamspinner.png", x, y) +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::DreamSpinner, room::Maple.Room) + Ahorn.drawSprite(ctx, "isafriend/danger/crystal/fg_dreamspinner.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/pause.jl b/Ahorn/entities/pause.jl new file mode 100644 index 0000000..d8f0f70 --- /dev/null +++ b/Ahorn/entities/pause.jl @@ -0,0 +1,23 @@ +module IsaGrabBagPauseCrystal + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/pauseCrystal" PauseCrystal(x::Integer, y::Integer) + +const placements = Ahorn.PlacementDict( + "Pause Crystal (IsaGrabBag)" => Ahorn.EntityPlacement( + PauseCrystal + ) +) + +function Ahorn.selection(entity::PauseCrystal) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle("objects/isafriend/pause/idle00.png", x, y) + +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::PauseCrystal, room::Maple.Room) + Ahorn.drawSprite(ctx, "isafriend/objects/pause/idle00.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/rewind.jl b/Ahorn/entities/rewind.jl new file mode 100644 index 0000000..4a840ef --- /dev/null +++ b/Ahorn/entities/rewind.jl @@ -0,0 +1,23 @@ +module IsaGrabBagRewindCrystal + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/rewindCrystal" RewindCrystal(x::Integer, y::Integer, oneUse::Bool=true) + +const placements = Ahorn.PlacementDict( + "Rewind Crystal (IsaGrabBag)" => Ahorn.EntityPlacement( + RewindCrystal + ) +) + +function Ahorn.selection(entity::RewindCrystal) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle("objects/isafriend/rewind/idle00.png", x, y) + +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::RewindCrystal, room::Maple.Room) + Ahorn.drawSprite(ctx, "isafriend/objects/rewind/idle00.png", 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/waterBoost.jl b/Ahorn/entities/waterBoost.jl new file mode 100644 index 0000000..f5deb09 --- /dev/null +++ b/Ahorn/entities/waterBoost.jl @@ -0,0 +1,29 @@ +module IsaGrabBagWaterBoost + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/waterBoost" WaterBoost(x::Integer, y::Integer, boostEnabled::Bool=true) + +const placements = Ahorn.PlacementDict( + "Water Boost Controller (IsaGrabBag)" => Ahorn.EntityPlacement( + WaterBoost, + "rectangle", + Dict{String, Any}( + "boostEnabled" => true + ) + ) +) + +sprite = "isafriend/helperimage.png" + +function Ahorn.selection(entity::WaterBoost) + x, y = Ahorn.position(entity) + return Ahorn.getSpriteRectangle(sprite, x, y) + +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::WaterBoost, room::Maple.Room) + Ahorn.drawSprite(ctx, sprite, 0, 0) +end + +end \ No newline at end of file diff --git a/Ahorn/entities/zipline.jl b/Ahorn/entities/zipline.jl new file mode 100644 index 0000000..ffc43e2 --- /dev/null +++ b/Ahorn/entities/zipline.jl @@ -0,0 +1,80 @@ +module IsaGrabBagZipline + +using ..Ahorn, Maple + +@mapdef Entity "isaBag/zipline" Zipline(x::Integer, y::Integer, nodes::Array{Tuple{Integer, Integer}, 1}=Tuple{Integer, Integer}[], usesStamina::Bool=true) + +const placements = Ahorn.PlacementDict( + "Zipline (IsaGrabBag)" => Ahorn.EntityPlacement( + Zipline, + "rectangle" + ) +) + +Ahorn.nodeLimits(entity::Zipline) = 0, 2 + +function Ahorn.selection(entity::Zipline) + + nodes = get(entity.data, "nodes", ()) + x, y = Ahorn.position(entity) + + res = [Ahorn.Rectangle(x - 8, y - 8, 16, 24)] + + for node in nodes + nx, ny = Int.(node) + + newRes = Ahorn.Rectangle(nx - 8, y - 8, 16, 24) + + push!(res, newRes) + end + + return res +end + +function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::Zipline, room::Maple.Room) + + ex, ey = Ahorn.position(entity) + + ey = ey - 1 + + nodes = get(entity.data, "nodes", ()) + + minx = ex + maxx = ex + + for node in nodes + nx, ny = Int.(node) + + if nx < minx + minx = nx + end + if nx > maxx + maxx = nx + end + end + + minx = minx - 10 + maxx = maxx + 10 + + Ahorn.drawRectangle(ctx, minx - ex, -2, maxx - minx, 4, (0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 0.0)) + + Ahorn.drawRectangle(ctx, minx - ex - 1, -4, 4, 8, (0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 0.0)) + Ahorn.drawRectangle(ctx, maxx - ex - 3, -4, 4, 8, (0.0, 0.0, 0.0, 1.0), (0.0, 0.0, 0.0, 0.0)) + + + Ahorn.drawRectangle(ctx, minx - ex, -1, maxx - minx, 1, (0.8, 0.84, 0.9, 1.0), (0.0, 0.0, 0.0, 0.0)) + Ahorn.drawRectangle(ctx, minx - ex, 0, maxx - minx, 1, (0.55, 0.5, 0.6, 1.0), (0.0, 0.0, 0.0, 0.0)) + + Ahorn.drawRectangle(ctx, minx - ex - 0, -3, 2, 6, (0.8, 0.84, 0.9, 1.0), (0.0, 0.0, 0.0, 0.0)) + Ahorn.drawRectangle(ctx, maxx - ex - 2, -3, 2, 6, (0.8, 0.84, 0.9, 1.0), (0.0, 0.0, 0.0, 0.0)) + + for node in nodes + nx, ny = Int.(node) + + Ahorn.drawSprite(ctx, "isafriend/objects/zipline/handle_end.png", nx - ex, 4) + end + + Ahorn.drawSprite(ctx, "isafriend/objects/zipline/handle.png", 0, 4) +end + +end \ No newline at end of file diff --git a/Ahorn/lang/en_gb.lang b/Ahorn/lang/en_gb.lang new file mode 100644 index 0000000..cdd18fa --- /dev/null +++ b/Ahorn/lang/en_gb.lang @@ -0,0 +1,31 @@ +# Arrow Bubble +placements.entities.isaBag/arrowBubble.tooltips.direction=The direction that the bubble will curve in. + +# Corner Boost Block +placements.entities.isaBag/cornerBlock.tooltips.tiletype=Changes the visual appearance of the block. +placements.entities.isaBag/cornerBlock.tooltips.useTileset=Whether to use the tiletype field instead of the default block texture. + +# Dream Spinner +placements.entities.isaBag/dreamSpinner.tooltips.useOnce=Whether the spinner should shatter after being dashed through. + +# Friendly Kevin +placements.entities.isaBag/arrowBlock.tooltips.distance=The maximum distance the Kevin can move, in pixels. +placements.entities.isaBag/arrowBlock.tooltips.inverted=Whether the Kevin should move in the opposite direction of the player's movement. +placements.entities.isaBag/arrowBlock.tooltips.movementRestriction=The axis the Kevin is allowed to move on. Use "no_limit" for free movement. + +# Rewind Crystal +placements.entities.isaBag/rewindCrystal.tooltips.oneUse=Whether the refill should respawn after being used. + +# Water Boost Controller +placements.entities.isaBag/waterBoost.tooltips.boostEnabled=Whether to enable waterboosting in the map. + +# Zipline +placements.entities.isaBag/zipline.tooltips.usesStamina=Whether riding the zipline or jumping from it should consume stamina. + +# Core Wind Trigger +placements.triggers.isaBag/coreWindTrigger.tooltips.patternHot=The wind pattern to use when the core mode is Hot. +placements.triggers.isaBag/coreWindTrigger.tooltips.patternCold=The wind pattern to use when the core mode is Cold. + +# Force Variant Trigger +placements.triggers.ForceVariantTrigger.tooltips.variantChange=The variant to change. +placements.triggers.ForceVariantTrigger.tooltips.enableStyle=The change to apply to the variant when entering the trigger.\n* Enabled/Disabled: turns the variant on or off.\n* Permanent: applies the change to the session so it persists after S&Q.\n* Temporary: reverts the change after leaving the trigger.\n* Toggle: changes the variant to the opposite of its current state.\n* SetToDefault: sets the variant to its initial state. \ No newline at end of file diff --git a/Ahorn/triggers/coreWindTrigger.jl b/Ahorn/triggers/coreWindTrigger.jl new file mode 100644 index 0000000..a5cdc8a --- /dev/null +++ b/Ahorn/triggers/coreWindTrigger.jl @@ -0,0 +1,21 @@ +module IsaGrabBagCoreWindTrigger + +using ..Ahorn, Maple + +@mapdef Trigger "isaBag/coreWindTrigger" CoreWindTrigger(x::Integer, y::Integer, width::Integer=16, height::Integer=16, patternHot::String="Up", patternCold::String="Down") + +const placements = Ahorn.PlacementDict( + "Core Wind Trigger (IsaGrabBag)" => Ahorn.EntityPlacement( + CoreWindTrigger, + "rectangle" + ) +) + +function Ahorn.editingOptions(trigger::CoreWindTrigger) + return Dict{String, Any}( + "patternHot" => Maple.wind_patterns, + "patternCold" => Maple.wind_patterns + ) +end + +end \ No newline at end of file diff --git a/Ahorn/triggers/forceVariantTrigger.jl b/Ahorn/triggers/forceVariantTrigger.jl new file mode 100644 index 0000000..e6b675f --- /dev/null +++ b/Ahorn/triggers/forceVariantTrigger.jl @@ -0,0 +1,39 @@ +module IsaGrabBagForceVariantTrigger + +using ..Ahorn, Maple + +@mapdef Trigger "ForceVariantTrigger" VariantTrigger(x::Integer, y::Integer, width::Integer=16, height::Integer=16, variantChange::String="Hiccups", enableStyle::String="EnabledPermanent") + +const variants = String[ + "Hiccups", + "InfiniteStamina", + "Invincible", + "InvisibleMotion", + "LowFriction", + "MirrorMode", + "NoGrabbing", + "PlayAsBadeline", + "SuperDashing", + "ThreeSixtyDashing", + "DashAssist" +] + +const variantMod = String[ + "Enabled", + "Disabled", + "EnabledPermanent", + "DisabledPermanent", + "EnabledTemporary", + "DisabledTemporary", + "Toggle", + "SetToDefault" +] + +function Ahorn.editingOptions(trigger::VariantTrigger) + return Dict{String, Any}( + "variantChange" => variants, + "enableStyle" => variantMod + ) +end + +end \ No newline at end of file diff --git a/ArrowBubble.cs b/ArrowBubble.cs deleted file mode 100644 index 363ae77..0000000 --- a/ArrowBubble.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Reflection; -using Microsoft.Xna.Framework; -using Monocle; - -namespace Celeste.Mod.IsaGrabBag { - public class ArrowBubble : Booster { - - static Vector2 GravityDir; - static FieldInfo sprite = typeof(Booster).GetField("sprite", BindingFlags.NonPublic | BindingFlags.Instance), - cannotUseTimer = typeof(Booster).GetField("cannotUseTimer", BindingFlags.NonPublic | BindingFlags.Instance), - respawnTimer = typeof(Booster).GetField("respawnTimer", BindingFlags.NonPublic | BindingFlags.Instance); - - private Sprite sprites; - - public static void Load() { - - On.Celeste.Player.RedDashEnd += Player_RedDashEnd; - On.Celeste.Player.RedDashUpdate += Player_RedDashUpdate; - } - - public static void Unload() { - - On.Celeste.Player.RedDashEnd -= Player_RedDashEnd; - On.Celeste.Player.RedDashUpdate -= Player_RedDashUpdate; - } - - private static void Player_RedDashEnd(On.Celeste.Player.orig_RedDashEnd orig, Player self) { - orig(self); - if (GravityDir != Vector2.Zero) { - self.UseRefill(false); - } - GravityDir = Vector2.Zero; - } - - private static int Player_RedDashUpdate(On.Celeste.Player.orig_RedDashUpdate orig, Player self) { - - if (self.CanDash && self.LastBooster != null) { - respawnTimer.SetValue(self.LastBooster, 1); - cannotUseTimer.SetValue(self.LastBooster, 0); - } - - int value = orig(self); - - if (GravityDir != Vector2.Zero) { - - float approachSpeed = 350 * Engine.DeltaTime; - - void changeValue(ref float val, float dir) { - val = Calc.Approach(val, (val * dir) >= 0 ? (360 * dir) : 0, approachSpeed); - } - - if (GravityDir.X != 0) - changeValue(ref self.Speed.X, GravityDir.X); - if (GravityDir.Y != 0) - changeValue(ref self.Speed.Y, GravityDir.Y); - - self.DashDir = self.Speed.SafeNormalize(); - } - - return value; - } - - public Vector2 gravityDirection; - - public ArrowBubble(EntityData data, Vector2 offset) : base(data.Position + offset, true) { - - string dir = data.Attr("direction", "down"); - - Add(new PlayerCollider(OnPlayer)); - - switch (dir) { - default: - dir = "down"; - gravityDirection = Vector2.UnitY; - break; - case "up": - gravityDirection = -Vector2.UnitY; - break; - case "left": - gravityDirection = -Vector2.UnitX; - break; - case "right": - gravityDirection = Vector2.UnitX; - break; - } - - Remove(Get()); - - sprites = GrabBagModule.sprites.Create($"booster_{dir}"); - Add(sprites); - sprite.SetValue(this, sprites); - } - - void OnPlayer(Player player) { - if (player.StateMachine != Player.StRedDash) { - GravityDir = gravityDirection; - } - sprites.FlipX = false; - } - } -} diff --git a/CelesteMod.csproj b/CelesteMod.csproj deleted file mode 100644 index 102ed46..0000000 --- a/CelesteMod.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - Debug - AnyCPU - {BD4DC57E-AB09-4291-8473-AB07471DDAFA} - Library - false - IsaMods - v4.5.2 - 512 - - - - true - full - false - C:\Program Files %28x86%29\Steam\steamapps\common\Celeste\Mods\IsaGrabBag\Code\ - TRACE - prompt - 4 - - - pdbonly - true - ..\..\..\..\..\Program Files %28x86%29\Steam\steamapps\common\Celeste\Mods\IsaGrabBag\Code\ - TRACE - prompt - 4 - - - Celeste.Mod.IsaGrabBag - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\Celeste.exe - False - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\FNA.dll - False - - - C:\Program Files (x86)\Steam\steamapps\common\Celeste\KeraLua.dll - False - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\MMHOOK_Celeste.dll - False - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\MonoMod.RuntimeDetour.dll - False - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\MonoMod.Utils.dll - False - - - C:\Program Files (x86)\Steam\steamapps\common\Celeste\NLua.dll - False - - - C:\Program Files (x86)\Steam\steamapps\common\Celeste\Mods Unzipped\OutbackHelper\OutbackHelper.dll - False - - - - - ..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\Celeste_OpenGL\YamlDotNet.dll - False - - - - - - - - - - \ No newline at end of file diff --git a/ArrowBlock.cs b/Code/ArrowBlock.cs similarity index 57% rename from ArrowBlock.cs rename to Code/ArrowBlock.cs index 6d32386..935113e 100644 --- a/ArrowBlock.cs +++ b/Code/ArrowBlock.cs @@ -1,76 +1,49 @@ -using System; -using System.IO; +using Celeste.Mod.Entities; using Microsoft.Xna.Framework; using Monocle; -using Celeste; -using Celeste.Mod; +using System; using System.Collections.Generic; -namespace Celeste.Mod.IsaGrabBag -{ - public class ArrowBlock : Solid - { - private const float MoveSpeed = 500, TurnSpeed = 750; - private const bool JoystickAlways = false; - - private enum ArrowDirection { - no_limit, - horizontal, - vertical, - cardinal, - diagonal - } - - private bool UseAnalog - { - get - { - return (JoystickAlways || SaveData.Instance.Assists.ThreeSixtyDashing) && (limitation != ArrowDirection.cardinal && limitation != ArrowDirection.diagonal); - } - } - - public int InvertVal { get { return Inverted ? -1 : 1; } } - public bool Inverted { get; private set; } - public int Distance { get; private set; } - +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/arrowBlock")] + public class ArrowBlock : Solid { + private const float MoveSpeed = 500; + private const float TurnSpeed = 750; + + private readonly ArrowDirection limitation; + private readonly List idleImages = new(); + private readonly List activeTopImages = new(); + private readonly List activeRightImages = new(); + private readonly List activeLeftImages = new(); + private readonly List activeBottomImages = new(); + private readonly Image face; private Vector2 moveDir, localPos, previousDirection; private Vector2 originalPosition; - private ArrowDirection limitation; - private Vector2 glowDirection; + private Image topleftCorner; + private Image toprightCorner; + private Image bottomleftCorner; + private Image bottomrightCorner; - public ArrowBlock(EntityData _data, Vector2 _offset) : base(_data.Position + _offset, _data.Width, _data.Height, false) - { + public ArrowBlock(EntityData _data, Vector2 _offset) + : base(_data.Position + _offset, _data.Width, _data.Height, false) { Distance = _data.Int("distance", 16); Inverted = _data.Bool("inverted", false); originalPosition = _data.Position + _offset; limitation = _data.Enum("movementRestriction", ArrowDirection.no_limit); - int num = (int)(base.Width / 8f) - 1; - int num2 = (int)(base.Height / 8f) - 1; + int num = (int)(Width / 8f) - 1; + int num2 = (int)(Height / 8f) - 1; List atlasSubtextures = GFX.Game.GetAtlasSubtextures("isafriend/objects/arrowblock/block"); - MTexture idle; - - switch (limitation) { - default: - idle = atlasSubtextures[0]; - break; - case ArrowDirection.horizontal: - idle = atlasSubtextures[1]; - break; - case ArrowDirection.vertical: - idle = atlasSubtextures[2]; - break; - case ArrowDirection.cardinal: - idle = atlasSubtextures[3]; - break; - case ArrowDirection.diagonal: - idle = atlasSubtextures[4]; - break; - } - + MTexture idle = limitation switch { + ArrowDirection.horizontal => atlasSubtextures[1], + ArrowDirection.vertical => atlasSubtextures[2], + ArrowDirection.cardinal => atlasSubtextures[3], + ArrowDirection.diagonal => atlasSubtextures[4], + _ => atlasSubtextures[0], + }; face = new Image(GFX.Game["isafriend/objects/arrowblock/idle_face"]); face.CenterOrigin(); face.Position = new Vector2(Width / 2, Height / 2); @@ -81,10 +54,12 @@ public ArrowBlock(EntityData _data, Vector2 _offset) : base(_data.Position + _of AddImage(idle, i, 0, Calc.Random.Choose(1, 2), 0, 0, -1); AddImage(idle, i, num2, Calc.Random.Choose(1, 2), 3, 0, 1); } + for (int j = 1; j < num2; j++) { AddImage(idle, 0, j, 0, Calc.Random.Choose(1, 2), -1, 0); AddImage(idle, num, j, 3, Calc.Random.Choose(1, 2), 1, 0); } + AddImage(idle, 0, 0, 0, 0, -1, -1); AddImage(idle, num, 0, 3, 0, 1, -1); AddImage(idle, 0, num2, 0, 3, -1, 1); @@ -93,223 +68,89 @@ public ArrowBlock(EntityData _data, Vector2 _offset) : base(_data.Position + _of Add(new LightOcclude(0.2f)); } - private Vector2 GetOffsetPosition() - { - if (GrabBagModule.playerInstance == null || GrabBagModule.playerInstance.Dead) - return Vector2.Zero; - - Vector2 move = UseAnalog ? Input.Feather.Value : new Vector2(Input.MoveX, Input.MoveY); - - if (move.X != 0 || move.Y != 0) - move.Normalize(); - - switch (limitation) { - default: - break; - case ArrowDirection.horizontal: - move.Y = 0; - if (move.X != 0) - move.X = Math.Sign(move.X); - break; - case ArrowDirection.vertical: - move.X = 0; - if (move.Y != 0) - move.Y = Math.Sign(move.Y); - break; - case ArrowDirection.cardinal: - if (move.X != 0 && move.Y != 0) { - move = Vector2.Zero; - } - - break; - case ArrowDirection.diagonal: - move = move.Rotate(MathHelper.PiOver4); - - if (Math.Abs(move.X) > 0.001f && Math.Abs(move.Y) > 0.001f) { - move = Vector2.Zero; - } - else { - move = move.Rotate(-MathHelper.PiOver4); - } - - - break; - } - - return move * InvertVal; + private enum ArrowDirection { + no_limit, + horizontal, + vertical, + cardinal, + diagonal } - public override void Update() - { + public int InvertVal => Inverted ? -1 : 1; + public bool Inverted { get; private set; } + public int Distance { get; private set; } + + public override void Update() { base.Update(); previousDirection = GetOffsetPosition(); - var newPos = GetOffsetPosition() * Distance; - var dir = newPos - localPos; - if (dir.X != 0 || dir.Y != 0) - { + Vector2 newPos = GetOffsetPosition() * Distance; + Vector2 dir = newPos - localPos; + if (dir.X != 0 || dir.Y != 0) { dir.Normalize(); } moveDir += dir * Engine.DeltaTime * TurnSpeed; - if (moveDir.Length() > MoveSpeed) - { - moveDir = (moveDir.SafeNormalize() * MoveSpeed); + if (moveDir.Length() > MoveSpeed) { + moveDir = moveDir.SafeNormalize() * MoveSpeed; } - if (Calc.AbsAngleDiff(moveDir.Angle(), dir.Angle()) > MathHelper.PiOver2) - { + + if (Calc.AbsAngleDiff(moveDir.Angle(), dir.Angle()) > MathHelper.PiOver2) { moveDir = Vector2.Zero; } - var movement = moveDir * Engine.DeltaTime; + Vector2 movement = moveDir * Engine.DeltaTime; - if (Vector2.Distance(localPos, newPos) < movement.Length()) - { + if (Vector2.Distance(localPos, newPos) < movement.Length()) { localPos = newPos; moveDir = Vector2.Zero; - } - else - { + } else { localPos += movement; } - if (localPos.Length() > Distance) - { + if (localPos.Length() > Distance) { localPos.Normalize(); localPos *= Distance; moveDir = Vector2.Zero; - } + } glowDirection = Calc.Approach(glowDirection, previousDirection, 10 * Engine.DeltaTime); - MoveTo(originalPosition + localPos); } - private void AddImage(MTexture idle, int x, int y, int tx, int ty, int borderX = 0, int borderY = 0) { - MTexture subtexture = idle.GetSubtexture(tx * 8, ty * 8, 8, 8, null); - Vector2 vector = new Vector2((float)(x * 8), (float)(y * 8)); - if (borderX != 0) { - Add(new Image(subtexture) { - Color = Color.Black, - Position = vector + new Vector2((float)borderX, 0f) - }); - } - if (borderY != 0) { - Add(new Image(subtexture) { - Color = Color.Black, - Position = vector + new Vector2(0f, (float)borderY) - }); - } - Image image = new Image(subtexture); - image.Position = vector; - Add(image); - idleImages.Add(image); - - if (borderX != 0 || borderY != 0) { - - if (borderX < 0 && (limitation != ArrowDirection.diagonal && limitation != ArrowDirection.vertical)) { - Image image2 = new Image(GFX.Game["isafriend/objects/arrowblock/lit_left"].GetSubtexture(0, ty * 8, 8, 8, null)); - activeLeftImages.Add(image2); - image2.Position = vector; - Add(image2); - } - else if (borderX > 0 && (limitation != ArrowDirection.diagonal && limitation != ArrowDirection.vertical)) { - Image image3 = new Image(GFX.Game["isafriend/objects/arrowblock/lit_right"].GetSubtexture(0, ty * 8, 8, 8, null)); - activeRightImages.Add(image3); - image3.Position = vector; - Add(image3); - } - if (borderY < 0 && (limitation != ArrowDirection.diagonal && limitation != ArrowDirection.horizontal)) { - Image image4 = new Image(GFX.Game["isafriend/objects/arrowblock/lit_top"].GetSubtexture(tx * 8, 0, 8, 8, null)); - activeTopImages.Add(image4); - image4.Position = vector; - Add(image4); - } - if (borderY > 0 && (limitation != ArrowDirection.diagonal && limitation != ArrowDirection.horizontal)) { - Image image5 = new Image(GFX.Game["isafriend/objects/arrowblock/lit_bottom"].GetSubtexture(tx * 8, 0, 8, 8, null)); - activeBottomImages.Add(image5); - image5.Position = vector; - Add(image5); - } - if (borderX < 0 && borderY < 0) { - topleftCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_topleft"]); - topleftCorner.Position = vector; - Add(topleftCorner); - } - if (borderX < 0 && borderY > 0) { - bottomleftCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_bottomleft"]); - bottomleftCorner.Position = vector; - Add(bottomleftCorner); - } - if (borderX > 0 && borderY < 0) { - toprightCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_topright"]); - toprightCorner.Position = vector; - Add(toprightCorner); - } - if (borderX > 0 && borderY > 0) { - bottomrightCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_bottomright"]); - bottomrightCorner.Position = vector; - Add(bottomrightCorner); - } - } - } - List idleImages = new List(); - List activeTopImages = new List(); - List activeRightImages = new List(); - List activeLeftImages = new List(); - List activeBottomImages = new List(); - Image topleftCorner, toprightCorner, bottomleftCorner, bottomrightCorner, face; public override void Render() { Rectangle rect = Collider.Bounds; - face.Position = new Vector2(Width / 2, Height / 2) + glowDirection * 2; + face.Position = new Vector2(Width / 2, Height / 2) + (glowDirection * 2); face.FlipY = Inverted; rect.Inflate(-3, -3); Draw.Rect(rect, Calc.HexToColor("483b69")); float lightStrength = Calc.ClampedMap(glowDirection.Y, 0, -.9f, 0, 1); - foreach (var img in activeTopImages) { + foreach (Image img in activeTopImages) { img.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); } lightStrength = Calc.ClampedMap(glowDirection.Y, 0, .9f, 0, 1); - foreach (var img in activeBottomImages) { + foreach (Image img in activeBottomImages) { img.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); } lightStrength = Calc.ClampedMap(glowDirection.X, 0, -.9f, 0, 1); - foreach (var img in activeLeftImages) { + foreach (Image img in activeLeftImages) { img.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); } lightStrength = Calc.ClampedMap(glowDirection.X, 0, .9f, 0, 1); - foreach (var img in activeRightImages) { + foreach (Image img in activeRightImages) { img.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); } switch (limitation) { - default: { - - Vector2 dir = Calc.Rotate(glowDirection, MathHelper.PiOver4); - - lightStrength = Calc.ClampedMap(dir.Y, 0, -.9f, 0, 1); - topleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - - lightStrength = Calc.ClampedMap(dir.X, 0, .9f, 0, 1); - toprightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - - lightStrength = Calc.ClampedMap(dir.X, 0, -.9f, 0, 1); - bottomleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - - lightStrength = Calc.ClampedMap(dir.Y, 0, .9f, 0, 1); - bottomrightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - } - break; - case ArrowDirection.horizontal: { - + case ArrowDirection.horizontal: lightStrength = Calc.ClampedMap(glowDirection.X, 0, -.9f, 0, 1); topleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); bottomleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); @@ -317,10 +158,9 @@ public override void Render() { lightStrength = Calc.ClampedMap(glowDirection.X, 0, .9f, 0, 1); toprightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); bottomrightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - } - break; - case ArrowDirection.vertical: { + break; + case ArrowDirection.vertical: lightStrength = Calc.ClampedMap(glowDirection.Y, 0, -.9f, 0, 1); topleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); toprightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); @@ -328,20 +168,187 @@ public override void Render() { lightStrength = Calc.ClampedMap(glowDirection.Y, 0, .9f, 0, 1); bottomleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); bottomrightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); - } - break; + + break; case ArrowDirection.cardinal: topleftCorner.Visible = false; toprightCorner.Visible = false; bottomleftCorner.Visible = false; bottomrightCorner.Visible = false; break; - } + default: + Vector2 dir = Calc.Rotate(glowDirection, MathHelper.PiOver4); + + lightStrength = Calc.ClampedMap(dir.Y, 0, -.9f, 0, 1); + topleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); + + lightStrength = Calc.ClampedMap(dir.X, 0, .9f, 0, 1); + toprightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); + + lightStrength = Calc.ClampedMap(dir.X, 0, -.9f, 0, 1); + bottomleftCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); + lightStrength = Calc.ClampedMap(dir.Y, 0, .9f, 0, 1); + bottomrightCorner.Color = Color.Lerp(Color.Transparent, Color.White, lightStrength * lightStrength * lightStrength); + break; + } base.Render(); + } + + /// + /// Get the direction the player is holding, snapped to eight directions and normalized, + /// or the zero vector if no direction is held. Mostly copied from Celeste.Input.GetAimVector(). + /// + /// A normalized vector on a cardinal or diagonal or the zero vector. + private static Vector2 GetEightDirectionalAim() { + Vector2 value = Input.Feather.Value; + if (value == Vector2.Zero) { + return Vector2.Zero; + } + + float angle = value.Angle(); + float angleThreshold = (float)Math.PI / 8f; + if (angle < 0) { + angleThreshold -= Calc.ToRad(5f); + } + + if (Calc.AbsAngleDiff(angle, 0f) < angleThreshold) { + return new Vector2(1f, 0f); + } else if (Calc.AbsAngleDiff(angle, (float)Math.PI) < angleThreshold) { + return new Vector2(-1f, 0f); + } else if (Calc.AbsAngleDiff(angle, -(float)Math.PI / 2f) < angleThreshold) { + return new Vector2(0f, -1f); + } else if (Calc.AbsAngleDiff(angle, (float)Math.PI / 2f) < angleThreshold) { + return new Vector2(0f, 1f); + } else { + return new Vector2(Math.Sign(value.X), Math.Sign(value.Y)).SafeNormalize(); + } + } + private Vector2 GetOffsetPosition() { + if (GrabBagModule.playerInstance == null || GrabBagModule.playerInstance.Dead) { + return Vector2.Zero; + } + + Vector2 move; + if (SaveData.Instance.Assists.ThreeSixtyDashing && limitation == ArrowDirection.no_limit) { + move = Input.Feather.Value.SafeNormalize(); + } else { + move = GetEightDirectionalAim(); + switch (limitation) { + case ArrowDirection.horizontal: + move.Y = 0; + if (move.X != 0) { + move.X = Math.Sign(move.X); + } + + break; + case ArrowDirection.vertical: + move.X = 0; + if (move.Y != 0) { + move.Y = Math.Sign(move.Y); + } + + break; + case ArrowDirection.cardinal: + if (move.X != 0 && move.Y != 0) { + move = Vector2.Zero; + } + + break; + case ArrowDirection.diagonal: + if (move.X == 0 || move.Y == 0) { + move = Vector2.Zero; + } + + break; + } + } + + return move * InvertVal; + } + + private void AddImage(MTexture idle, int x, int y, int tx, int ty, int borderX = 0, int borderY = 0) { + MTexture subtexture = idle.GetSubtexture(tx * 8, ty * 8, 8, 8, null); + Vector2 vector = new(x * 8, y * 8); + if (borderX != 0) { + Add(new Image(subtexture) { + Color = Color.Black, + Position = vector + new Vector2(borderX, 0f) + }); + } + + if (borderY != 0) { + Add(new Image(subtexture) { + Color = Color.Black, + Position = vector + new Vector2(0f, borderY) + }); + } + + Image image = new(subtexture) { + Position = vector + }; + Add(image); + idleImages.Add(image); + + if (borderX != 0 || borderY != 0) { + + if (borderX < 0 && limitation != ArrowDirection.diagonal && limitation != ArrowDirection.vertical) { + Image image2 = new(GFX.Game["isafriend/objects/arrowblock/lit_left"].GetSubtexture(0, ty * 8, 8, 8, null)); + activeLeftImages.Add(image2); + image2.Position = vector; + Add(image2); + } else if (borderX > 0 && limitation != ArrowDirection.diagonal && limitation != ArrowDirection.vertical) { + Image image3 = new(GFX.Game["isafriend/objects/arrowblock/lit_right"].GetSubtexture(0, ty * 8, 8, 8, null)); + activeRightImages.Add(image3); + image3.Position = vector; + Add(image3); + } + + if (borderY < 0 && limitation != ArrowDirection.diagonal && limitation != ArrowDirection.horizontal) { + Image image4 = new(GFX.Game["isafriend/objects/arrowblock/lit_top"].GetSubtexture(tx * 8, 0, 8, 8, null)); + activeTopImages.Add(image4); + image4.Position = vector; + Add(image4); + } + + if (borderY > 0 && limitation != ArrowDirection.diagonal && limitation != ArrowDirection.horizontal) { + Image image5 = new(GFX.Game["isafriend/objects/arrowblock/lit_bottom"].GetSubtexture(tx * 8, 0, 8, 8, null)); + activeBottomImages.Add(image5); + image5.Position = vector; + Add(image5); + } + + if (borderX < 0 && borderY < 0) { + topleftCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_topleft"]) { + Position = vector + }; + Add(topleftCorner); + } + + if (borderX < 0 && borderY > 0) { + bottomleftCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_bottomleft"]) { + Position = vector + }; + Add(bottomleftCorner); + } + + if (borderX > 0 && borderY < 0) { + toprightCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_topright"]) { + Position = vector + }; + Add(toprightCorner); + } + + if (borderX > 0 && borderY > 0) { + bottomrightCorner = new Image(GFX.Game["isafriend/objects/arrowblock/lit_bottomright"]) { + Position = vector + }; + Add(bottomrightCorner); + } + } } } } \ No newline at end of file diff --git a/Code/ArrowBubble.cs b/Code/ArrowBubble.cs new file mode 100644 index 0000000..17d0c37 --- /dev/null +++ b/Code/ArrowBubble.cs @@ -0,0 +1,96 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Monocle; +using MonoMod.Utils; + +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/arrowBubble")] + public class ArrowBubble : Booster { + public Vector2 gravityDirection; + private readonly Sprite sprite; + private static Vector2 GravityDir; + + public ArrowBubble(EntityData data, Vector2 offset) + : base(data.Position + offset, true) { + string dir = data.Attr("direction", "down"); + + Add(new PlayerCollider(OnPlayer)); + + switch (dir) { + default: + dir = "down"; + gravityDirection = Vector2.UnitY; + break; + case "up": + gravityDirection = -Vector2.UnitY; + break; + case "left": + gravityDirection = -Vector2.UnitX; + break; + case "right": + gravityDirection = Vector2.UnitX; + break; + } + + DynamicData baseData = new(typeof(Booster), this); + Remove(Get()); + Add(sprite = GrabBagModule.sprites.Create($"booster_{dir}")); + baseData.Set("sprite", sprite); + + } + + public static void Load() { + On.Celeste.Player.RedDashEnd += Player_RedDashEnd; + On.Celeste.Player.RedDashUpdate += Player_RedDashUpdate; + } + + public static void Unload() { + On.Celeste.Player.RedDashEnd -= Player_RedDashEnd; + On.Celeste.Player.RedDashUpdate -= Player_RedDashUpdate; + } + + private static void Player_RedDashEnd(On.Celeste.Player.orig_RedDashEnd orig, Player self) { + orig(self); + if (GravityDir != Vector2.Zero) { + GravityDir = Vector2.Zero; + self.UseRefill(twoDashes: false); + } + } + + private static int Player_RedDashUpdate(On.Celeste.Player.orig_RedDashUpdate orig, Player self) { + if (self.CanDash && self.LastBooster != null) { + DynamicData boosterData = DynamicData.For(self.LastBooster); + boosterData.Set("respawnTimer", 1f); + boosterData.Set("cannotUseTimer", 0f); + } + + int value = orig(self); + if (GravityDir != Vector2.Zero) { + float approachSpeed = 350 * Engine.DeltaTime; + + void changeValue(ref float val, float dir) { + val = Calc.Approach(val, (val * dir) >= 0 ? (360 * dir) : 0, approachSpeed); + } + + if (GravityDir.X != 0) { + changeValue(ref self.Speed.X, GravityDir.X); + } + + if (GravityDir.Y != 0) { + changeValue(ref self.Speed.Y, GravityDir.Y); + } + + self.DashDir = self.Speed.SafeNormalize(); + } + + return value; + } + + private void OnPlayer(Player player) { + sprite.FlipX = false; + if (player.StateMachine != Player.StRedDash) { + GravityDir = gravityDirection; + } + } + } +} diff --git a/BadelineFollower.cs b/Code/BadelineFollower.cs similarity index 59% rename from BadelineFollower.cs rename to Code/BadelineFollower.cs index c6e3a2e..3fbc935 100644 --- a/BadelineFollower.cs +++ b/Code/BadelineFollower.cs @@ -1,120 +1,222 @@ -using Monocle; -using Microsoft.Xna.Framework; -using System.Collections.Generic; -using System.Reflection; -using System.Collections; +using Microsoft.Xna.Framework; +using Monocle; +using MonoMod.Utils; using System; +using System.Collections; +using System.Collections.Generic; -namespace Celeste.Mod.IsaGrabBag -{ - public class BadelineFollower : Entity - { - public const string SESSION_FLAG = "has_badeline_follower"; +namespace Celeste.Mod.IsaGrabBag { + public class BadelineFollower : Entity { + public const string IsaGrabBag_HasBadelineFollower = "has_badeline_follower"; private static BadelineBoost booster; private static bool firstBoost = false; private static bool boosting = false; + private static Coroutine LookForBubble; + + private float previousPosition; + + public BadelineFollower(Level level, BadelineDummy _dummy, Vector2 position) + : base(position) { + level.Session.SetFlag(IsaGrabBag_HasBadelineFollower, true); + + level.Add(dummy = _dummy); + dummy.Add(follower = new Follower()); + follower.PersistentFollow = true; + follower.Added(dummy); + + AddTag(Tags.Persistent); + dummy.AddTag(Tags.Persistent); + + instance = this; + } + + public BadelineFollower(Level level, Vector2 position) + : this(level, new BadelineDummy(position), position) { + } + + public Follower follower { get; private set; } + public static BadelineFollower instance { get; set; } + public BadelineDummy dummy { get; private set; } + + [Command("spawn_follower", "spawn badeline follower")] + public static void CmdSpawnBadeline() { + if (Engine.Scene is Level level) { + SpawnBadelineFriendo(level); + } + } + + public static void Search() { + if (LookForBubble != null && LookForBubble.Active) { + LookForBubble.Cancel(); + } + + GrabBagModule.playerInstance.Add(LookForBubble = new Coroutine(SearchForBadeline(Engine.Scene as Level))); + } + + public static void SpawnBadelineFriendo(Level _level) { + Player player = GrabBagModule.playerInstance; + + if (_level == null) { + return; + } + + _level.Session.SetFlag(IsaGrabBag_HasBadelineFollower, true); + + if (player == null) { + return; + } - static Coroutine LookForBubble; + BadelineFollower follower = new(_level, player.Position); + _level.Add(follower); + player.Leader.GainFollower(follower.follower); + } - public static bool CheckBooster(Level lvl, bool onTransition) - { - if (lvl == null) + public static bool CheckBooster(Level lvl, bool onTransition) { + if (lvl == null) { return false; - if (!lvl.Session.GetFlag(SESSION_FLAG)) + } + + if (!lvl.Session.GetFlag(IsaGrabBag_HasBadelineFollower)) { return false; + } - var player = GrabBagModule.playerInstance; + Player player = GrabBagModule.playerInstance; booster = null; List boosters = lvl.Entities.FindAll(); float distTemp = float.MaxValue; - if (boosters.Count > 0) - { - if (player != null) - { + if (boosters.Count > 0) { + if (player != null) { Vector2 position = lvl.Session.RespawnPoint == null ? player.Position : lvl.Session.RespawnPoint.Value; float min = float.MaxValue; - foreach (BadelineBoost b in boosters) - { + foreach (BadelineBoost b in boosters) { b.Visible = false; distTemp = Vector2.Distance(b.Position, position); - if (distTemp < min) - { + if (distTemp < min) { min = distTemp; - if (booster != null) + if (booster != null) { booster.RemoveSelf(); + } + booster = b; - } - else + } else { b.RemoveSelf(); + } } booster.Collidable = false; booster.Visible = true; - if (lvl.Session.GetFlag(SESSION_FLAG)) - { + if (lvl.Session.GetFlag(IsaGrabBag_HasBadelineFollower)) { booster.Get().OnCollide = NewBoostMechanic; booster.Visible = false; firstBoost = true; - booster.Position = BadelineFollower.instance.Position; + booster.Position = instance.Position; booster.Add(new Coroutine(NewBoost())); - if (!lvl.Session.Level.Contains("_sl")) + if (!lvl.Session.Level.Contains("_sl")) { booster.Add(new Coroutine(Skip())); + } return true; - } - else - { + } else { booster.RemoveSelf(); } - } - else - { - foreach (BadelineBoost b in boosters) + } else { + foreach (BadelineBoost b in boosters) { b.RemoveSelf(); + } } } + return false; } - private static IEnumerator Skip() - { - var player = GrabBagModule.playerInstance; - FieldInfo index = typeof(BadelineBoost).GetField("nodeIndex", BindingFlags.NonPublic | BindingFlags.Instance); - FieldInfo travelling = typeof(BadelineBoost).GetField("travelling", BindingFlags.NonPublic | BindingFlags.Instance); - MethodInfo skip = typeof(BadelineBoost).GetMethod("Skip", BindingFlags.Instance | BindingFlags.NonPublic); + public override void Update() { + if (GrabBagModule.playerInstance != null && (follower.Leader == null || follower.Leader.Entity != GrabBagModule.playerInstance)) { + GrabBagModule.playerInstance.Leader.GainFollower(follower); + } + + if ((previousPosition - dummy.Position.X) * dummy.Sprite.Scale.X > 0) { + dummy.Sprite.Scale.X *= -1; + } + + previousPosition = dummy.Position.X; + + base.Update(); + } + + public override void SceneBegin(Scene scene) { + boosting = false; + base.SceneBegin(scene); + } + + public void Readd(Level lvl, Player obj) { + lvl.Add(this); + lvl.Add(dummy); + obj.Leader.GainFollower(follower); + dummy.Position = obj.Position - new Vector2(obj.Facing == Facings.Left ? -5 : 5, 16); + } - Vector2[] nodes = typeof(BadelineBoost).GetField("nodes", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(booster) as Vector2[]; + internal static void Load() { + Everest.Events.Level.OnLoadEntity += Level_OnLoadEntity; + On.Celeste.BadelineBoost.Awake += BadelineBoostAwake; + } + + internal static void Unload() { + Everest.Events.Level.OnLoadEntity -= Level_OnLoadEntity; + On.Celeste.BadelineBoost.Awake -= BadelineBoostAwake; + } + + private static bool Level_OnLoadEntity(Level level, LevelData levelData, Vector2 offset, EntityData entityData) { + switch (entityData.Name) { + case "isaBag/baddyFollow": + if (!level.Session.GetFlag(IsaGrabBag_HasBadelineFollower)) { + SpawnBadelineFriendo(level); + } + + return true; + } + + return false; + } + + private static void BadelineBoostAwake(On.Celeste.BadelineBoost.orig_Awake orig, BadelineBoost self, Scene scene) { + orig(self, scene); + if ((scene as Level).Session.GetFlag(IsaGrabBag_HasBadelineFollower)) { + self.Visible = false; + } + } + + private static IEnumerator Skip() { + Player player = GrabBagModule.playerInstance; + + DynamicData boostData = DynamicData.For(booster); + Vector2[] nodes = boostData.Get("nodes"); - int value; float distNow, distNext; yield return 1; - while ((value = (int)index.GetValue(booster)) < nodes.Length - 1) - { - while (boosting) - { + int value = boostData.Get("nodeIndex"); + while (value < nodes.Length - 1) { + while (boosting) { yield return null; continue; } distNow = Vector2.Distance(nodes[value], player.Position); distNext = Vector2.Distance(nodes[value + 1], player.Position); - if (distNow > distNext * 1.3f) - { - index.SetValue(booster, value); - skip.Invoke(booster, new object[0]); + if (distNow > distNext * 1.3f) { + boostData.Set("nodeIndex", value++); + boostData.Invoke("Skip"); boosting = true; - do - { - boosting = (bool)travelling.GetValue(booster); + do { + boosting = boostData.Get("travelling"); yield return null; } while (boosting); @@ -123,17 +225,15 @@ private static IEnumerator Skip() yield return null; } - yield break; } - private static IEnumerator SearchForBadeline(Level level) - { + private static IEnumerator SearchForBadeline(Level level) { bool exit = false; - while (!exit) - { - if (CheckBooster(level, true)) + while (!exit) { + if (CheckBooster(level, true)) { exit = true; + } yield return 0.05f; } @@ -141,88 +241,84 @@ private static IEnumerator SearchForBadeline(Level level) yield break; } - private static void NewBoostMechanic(Player obj) - { - if (booster == null) + private static void NewBoostMechanic(Player obj) { + if (booster == null) { throw new NullReferenceException("Badeline Booster is null?"); + } booster.Collidable = false; booster.Add(new Coroutine(NewBoost())); } - private static IEnumerator NewBoost() - { - var player = GrabBagModule.playerInstance; + private static IEnumerator NewBoost() { + Player player = GrabBagModule.playerInstance; - while (boosting) + while (boosting) { yield return 0; + } boosting = true; booster.Visible = true; - Type type = typeof(BadelineBoost); - - Vector2[] nodes = type.GetField("nodes", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(booster) as Vector2[]; - int nodeIndex = (int)type.GetField("nodeIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(booster); - Sprite sprite = type.GetField("sprite", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(booster) as Sprite; - Image stretch = type.GetField("stretch", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(booster) as Image; + DynamicData boostData = DynamicData.For(booster); + int nodeIndex = boostData.Get("nodeIndex"); + Vector2[] nodes = boostData.Get("nodes"); + Sprite sprite = boostData.Get("sprite"); + Image stretch = boostData.Get("stretch"); Level level = booster.Scene as Level; - if (BadelineFollower.instance.dummy.Visible) - { - BadelineFollower.instance.dummy.Visible = false; + if (instance.dummy.Visible) { + instance.dummy.Visible = false; } - if (!firstBoost) - type.GetField("nodeIndex", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(booster, ++nodeIndex); + if (!firstBoost) { + boostData.Set("nodeIndex", ++nodeIndex); + } bool finalBoost = nodeIndex >= nodes.Length; - if (!firstBoost) - { + if (!firstBoost) { sprite.Visible = false; sprite.Position = Vector2.Zero; booster.Collidable = false; Audio.Play("event:/char/badeline/booster_begin", booster.Position); - player.StateMachine.State = 11; + player.StateMachine.State = Player.StDummy; player.DummyAutoAnimate = false; player.DummyGravity = false; player.Dashes = 1; player.RefillStamina(); player.Speed = Vector2.Zero; int num = Math.Sign(player.X - booster.X); - if (num == 0) - { + if (num == 0) { num = -1; } - BadelineDummy badeline = new BadelineDummy(booster.Position); + BadelineDummy badeline = new(booster.Position); booster.Scene.Add(badeline); player.Facing = (Facings)(-num); - badeline.Sprite.Scale.X = (float)num; + badeline.Sprite.Scale.X = num; Vector2 playerFrom = player.Position; - Vector2 playerTo = booster.Position + new Vector2((float)(num * 4), -3f); + Vector2 playerTo = booster.Position + new Vector2(num * 4, -3f); Vector2 badelineFrom = badeline.Position; Vector2 badelineTo = booster.Position + new Vector2((float)(-(float)num * 4), 3f); - - for (float p = 0f; p < 1f; p += Engine.DeltaTime / 0.2f) - { + for (float p = 0f; p < 1f; p += Engine.DeltaTime / 0.2f) { Vector2 vector = Vector2.Lerp(playerFrom, playerTo, p); - if (player.Scene != null) - { + if (player.Scene != null) { player.MoveToX(vector.X, null); } - if (player.Scene != null) - { + + if (player.Scene != null) { player.MoveToY(vector.Y, null); } + badeline.Position = Vector2.Lerp(badelineFrom, badelineTo, p); yield return null; } + playerFrom = default; playerTo = default; badelineFrom = default; @@ -234,19 +330,17 @@ private static IEnumerator NewBoost() yield return 0.1f; - if (!player.Dead) - { + if (!player.Dead) { player.MoveV(5f, null, null); } yield return 0.1f; - booster.Add(Alarm.Create(Alarm.AlarmMode.Oneshot, delegate - { - if (player.Dashes < player.Inventory.Dashes) - { + booster.Add(Alarm.Create(Alarm.AlarmMode.Oneshot, delegate { + if (player.Dashes < player.Inventory.Dashes) { player.Dashes++; } + booster.Scene.Remove(badeline); (booster.Scene as Level).Displacement.AddBurst(badeline.Position, 0.25f, 8f, 32f, 0.5f, null, null); }, 0.15f, true)); @@ -258,42 +352,38 @@ private static IEnumerator NewBoost() booster.Visible = true; - Vector2 from = firstBoost ? BadelineFollower.instance.dummy.Position : booster.Position; - Vector2 to = finalBoost ? BadelineFollower.instance.dummy.Position : nodes[nodeIndex]; + Vector2 from = firstBoost ? instance.dummy.Position : booster.Position; + Vector2 to = finalBoost ? instance.dummy.Position : nodes[nodeIndex]; float duration = Vector2.Distance(from, to) / 320f; stretch.Visible = true; stretch.Rotation = (to - from).Angle(); Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.SineInOut, duration, true); - tween.OnUpdate = delegate (Tween t) - { - if (finalBoost) - to = BadelineFollower.instance.dummy.Position; + tween.OnUpdate = delegate (Tween t) { + if (finalBoost) { + to = instance.dummy.Position; + } booster.Position = Vector2.Lerp(from, to, t.Eased); - stretch.Scale.X = 1f + Calc.YoYo(t.Eased) * 2f; - stretch.Scale.Y = 1f - Calc.YoYo(t.Eased) * 0.75f; - if (t.Eased < 0.9f && booster.Scene.OnInterval(0.03f)) - { + stretch.Scale.X = 1f + (Calc.YoYo(t.Eased) * 2f); + stretch.Scale.Y = 1f - (Calc.YoYo(t.Eased) * 0.75f); + if (t.Eased < 0.9f && booster.Scene.OnInterval(0.03f)) { TrailManager.Add(booster, Player.TwoDashesHairColor, 0.5f); level.ParticlesFG.Emit(BadelineBoost.P_Move, 1, booster.Center, Vector2.One * 4f); } }; - tween.OnComplete = delegate (Tween t) - { + tween.OnComplete = delegate (Tween t) { stretch.Visible = false; - if (finalBoost) - { - BadelineFollower.instance.dummy.Visible = true; + if (finalBoost) { + instance.dummy.Visible = true; - } - else - { + } else { booster.Visible = true; sprite.Visible = true; booster.Collidable = true; } + Audio.Play("event:/char/badeline/booster_reappear", booster.Position); }; booster.Add(tween); @@ -306,88 +396,5 @@ private static IEnumerator NewBoost() boosting = false; yield break; } - - public static void Search() - { - if (LookForBubble != null && LookForBubble.Active) - LookForBubble.Cancel(); - - GrabBagModule.playerInstance.Add(LookForBubble = new Coroutine(SearchForBadeline(Engine.Scene as Level))); - } - - [Command("spawn_follower", "spawn badeline follower")] - public static void CmdSpawnBadeline() - { - Level level = Engine.Scene as Level; - SpawnBadelineFriendo(level); - } - public static void SpawnBadelineFriendo(Level _level) - { - var player = GrabBagModule.playerInstance; - - if (_level == null) - return; - - _level.Session.SetFlag(SESSION_FLAG, true); - - if (player == null) - return; - - BadelineFollower follower = new BadelineFollower(_level, player.Position); - _level.Add(follower); - player.Leader.GainFollower(follower.follower); - } - - public Follower follower { get; private set; } - public static BadelineFollower instance { get; set; } - public BadelineDummy dummy { get; private set; } - private float previousPosition; - - public override void SceneBegin(Scene scene) - { - boosting = false; - base.SceneBegin(scene); - } - - public void Readd(Level lvl, Player obj) - { - lvl.Add(this); - lvl.Add(dummy); - obj.Leader.GainFollower(follower); - dummy.Position = obj.Position - new Vector2(obj.Facing == Facings.Left ? -5 : 5, 16); - } - - public BadelineFollower(Level level, Vector2 position) : this(level, new BadelineDummy(position), position) - { - } - public BadelineFollower(Level level, BadelineDummy _dummy, Vector2 position) : base(position) - { - level.Session.SetFlag(SESSION_FLAG, true); - - level.Add(dummy = _dummy); - dummy.Add(follower = new Follower()); - follower.PersistentFollow = true; - follower.Added(dummy); - - AddTag(Tags.Persistent); - dummy.AddTag(Tags.Persistent); - - instance = this; - } - - public override void Update() - { - if (GrabBagModule.playerInstance != null && (follower.Leader == null || follower.Leader.Entity != GrabBagModule.playerInstance)) - GrabBagModule.playerInstance.Leader.GainFollower(follower); - - if ((previousPosition - dummy.Position.X) * dummy.Sprite.Scale.X > 0) - { - dummy.Sprite.Scale.X *= -1; - } - previousPosition = dummy.Position.X; - - base.Update(); - } - } } diff --git a/Code/CornerBoostBlock.cs b/Code/CornerBoostBlock.cs new file mode 100644 index 0000000..d2b6a12 --- /dev/null +++ b/Code/CornerBoostBlock.cs @@ -0,0 +1,217 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Monocle; +using MonoMod.Utils; +using System; +using System.Collections.Generic; + +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/cornerBlock")] + [Tracked(false)] + public class CornerBoostBlock : Solid { + private readonly char _tileset; + private readonly List Group = new(); + private TileGrid tiles; + private bool HasGroup; + private Point GroupBoundsMin, GroupBoundsMax; + + public CornerBoostBlock(Vector2 position, int width, int height, char tile, bool useTileset) + : base(position, width, height, true) { + _tileset = useTileset ? tile : '\0'; + OnCollide = OnCollision; + } + + public CornerBoostBlock(EntityData data, Vector2 offset) + : this(data.Position + offset, data.Width, data.Height, data.Char("tiletype", '3'), data.Bool("useTileset", false)) { + } + + public bool CustomTile => _tileset != '\0'; + + public static void Load() { + On.Celeste.Player.ClimbJump += OnClimbJumped; + On.Celeste.Player.ClimbEnd += Player_ClimbEnd; + On.Celeste.Player.NormalEnd += Player_NormalEnd; + } + + public static void Unload() { + On.Celeste.Player.ClimbJump -= OnClimbJumped; + On.Celeste.Player.ClimbEnd -= Player_ClimbEnd; + On.Celeste.Player.NormalEnd -= Player_NormalEnd; + } + + public override void Awake(Scene scene) { + if (!HasGroup) { + GroupBoundsMin = new Point((int)X, (int)Y); + GroupBoundsMax = new Point((int)Right, (int)Bottom); + + AddToGroupAndFindChildren(this); + + Rectangle rectangle = new(GroupBoundsMin.X / 8, GroupBoundsMin.Y / 8, (GroupBoundsMax.X - GroupBoundsMin.X) / 8, (GroupBoundsMax.Y - GroupBoundsMin.Y) / 8); + VirtualMap virtualMap = new(rectangle.Width, rectangle.Height, '0'); + + foreach (CornerBoostBlock block in Group) { + int left = (int)(block.X / 8f) - rectangle.X; + int top = (int)(block.Y / 8f) - rectangle.Y; + int right = (int)(block.Width / 8f); + int bottom = (int)(block.Height / 8f); + + for (int x = left; x < left + right; x++) { + for (int y = top; y < top + bottom; y++) { + + virtualMap[x, y] = CustomTile ? _tileset : '3'; + } + } + } + + tiles = GFX.FGAutotiler.GenerateMap(virtualMap, new Autotiler.Behaviour { + EdgesExtend = false, + EdgesIgnoreOutOfLevel = false, + PaddingIgnoreOutOfLevel = false + }).TileGrid; + + if (!CustomTile) { + Vector2 texOffset = new(0.255127f, 0.4956055f); + Vector2 texSize = new(0.01164f, 0.0291f); + const int width = 6, height = 15; + + TileGrid template = tiles; + tiles = new TileGrid(8, 8, rectangle.Width, rectangle.Height); + + MTexture tex = GFX.Game["isafriend/tilesets/boost_block"]; + Tileset tileset = new(tex, 8, 8); + + for (int y = 0; y < rectangle.Height; ++y) { + for (int x = 0; x < rectangle.Width; ++x) { + + MTexture fromTemplate = template.Tiles[x, y]; + + if (fromTemplate == null) { + continue; + } + + float u = (fromTemplate.LeftUV - texOffset.X) / texSize.X; + float v = (fromTemplate.TopUV - texOffset.Y) / texSize.Y; + + if (u is < 0 and > (-0.0001f)) { + u = 0; + } + + if (v is < 0 and > (-0.0001f)) { + v = 0; + } + + if (u < 0 || v < 0 || u >= 1 || v >= 1) { + break; + } + + tiles.Tiles[x, y] = tileset[(int)(u * width), (int)(v * height)]; + } + } + } + + tiles.Position = new Vector2(GroupBoundsMin.X - X, GroupBoundsMin.Y - Y); + + Add(tiles); + } + + base.Awake(scene); + } + + private static Rectangle GetFacingHitbox(Player player) { + Rectangle hitbox = player.Collider.Bounds; + hitbox.Width = 1; + + if (player.Facing == Facings.Left) { + hitbox.X -= 1; + } else { + hitbox.X += (int)player.Width; + } + + hitbox.Y -= 1; + hitbox.Height += 1; + + return hitbox; + } + + private static void Player_ClimbEnd(On.Celeste.Player.orig_ClimbEnd orig, Player self) { + float timer = DynamicData.For(self).Get("wallSpeedRetentionTimer"); + + orig(self); + + if (self.Scene.CollideCheck(GetFacingHitbox(self))) { + DynamicData.For(self).Set("wallSpeedRetentionTimer", timer); + } + } + + private static void Player_NormalEnd(On.Celeste.Player.orig_NormalEnd orig, Player self) { + float timer = DynamicData.For(self).Get("wallSpeedRetentionTimer"); + + orig(self); + + if (self.StateMachine.State == Player.StClimb && self.Scene.CollideCheck(GetFacingHitbox(self))) { + DynamicData.For(self).Set("wallSpeedRetentionTimer", timer); + } + } + + private static void OnClimbJumped(On.Celeste.Player.orig_ClimbJump orig, Player self) { + orig(self); + + float timer = DynamicData.For(self).Get("wallSpeedRetentionTimer"); + if (self.Scene.CollideCheck(GetFacingHitbox(self)) && timer > 0f) { + DynamicData.For(self).Set("wallSpeedRetentionTimer", Math.Max(timer, 0.06f)); + + float speed = DynamicData.For(self).Get("wallSpeedRetained"); + int moveX = DynamicData.For(self).Get("moveX"); + DynamicData.For(self).Set("retentionSpeed", speed + (40 * moveX)); + } + } + + private void OnCollision(Vector2 dir) { + + Player player = GrabBagModule.playerInstance; + player ??= Scene.Entities.FindFirst(); + + if (dir.X == 0 || player == null) { + return; + } + + if (DynamicData.For(player).Get("wallSpeedRetentionTimer") == 0.06f) { + DynamicData.For(player).Set("wallSpeedRetentionTimer", 0.12f); + } + } + + private void AddToGroupAndFindChildren(CornerBoostBlock from) { + if (from.X < GroupBoundsMin.X) { + GroupBoundsMin.X = (int)from.X; + } + + if (from.Y < GroupBoundsMin.Y) { + GroupBoundsMin.Y = (int)from.Y; + } + + if (from.Right > GroupBoundsMax.X) { + GroupBoundsMax.X = (int)from.Right; + } + + if (from.Bottom > GroupBoundsMax.Y) { + GroupBoundsMax.Y = (int)from.Bottom; + } + + from.HasGroup = true; + Group.Add(from); + if (from != this) { + //from.master = this; + } + + foreach (Entity entity in Scene.Tracker.GetEntities()) { + CornerBoostBlock block = (CornerBoostBlock)entity; + + if (!block.HasGroup && block._tileset == _tileset && + (Scene.CollideCheck(new Rectangle((int)from.X - 1, (int)from.Y, (int)from.Width + 2, (int)from.Height), block) || + Scene.CollideCheck(new Rectangle((int)from.X, (int)from.Y - 1, (int)from.Width, (int)from.Height + 2), block))) { + AddToGroupAndFindChildren(block); + } + } + } + } +} \ No newline at end of file diff --git a/Code/DreamSpinner.cs b/Code/DreamSpinner.cs new file mode 100644 index 0000000..a32f826 --- /dev/null +++ b/Code/DreamSpinner.cs @@ -0,0 +1,326 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Monocle; +using System.Collections.Generic; + +namespace Celeste.Mod.IsaGrabBag { + [Tracked] + [CustomEntity("isaBag/dreamSpinner", "isaBag/dreamSpinFake")] + public class DreamSpinner : Entity { + private static readonly Color debrisColor = Calc.HexToColor("c18a53"); + + public bool OneUse; + public bool Fake; + public bool ShouldRender; + + internal float rotation; + internal Color color; + internal List offsets; + + private readonly int ID; + private DreamBlock block; + private bool hasCollided; + + public DreamSpinner(Vector2 position, bool _useOnce, bool _fake) + : base(position) { + Collider = new ColliderList(new Collider[] { + new Circle(6f, 0f, 0f), + new Hitbox(16f, 4f, -8f, -3f) + }); + + Add(new PlayerCollider(OnPlayer)); + Add(new LedgeBlocker()); + + OneUse = _useOnce; + Fake = _fake; + Depth = -8499; // Update just before our renderer + Collidable = false; + Visible = false; + + rotation = Calc.Random.Choose(0, 1, 2, 3) * MathHelper.PiOver2; + offsets = new List(); + } + + public DreamSpinner(EntityData data, Vector2 offset) + : this(data.Position + offset, data.Bool("useOnce", false), _fake: data.Name == "isaBag/dreamSpinFake") { + ID = data.ID; + } + + public override void Added(Scene scene) { + base.Added(scene); + + if (!Fake) { + scene.Add(block = new DreamBlock(Center - new Vector2(8, 8), 16, 16, null, false, false)); + block.Visible = false; + + if (GrabBagModule.GrabBagMeta.RoundDreamSpinner) { + block.Collider = new Circle(9f, 8, 8); + } + } + } + + public override void Awake(Scene scene) { + base.Awake(scene); + + color = Color.Black; + if (!SceneAs().Session.Inventory.DreamDash) { + color = new Color(25, 25, 25); + } else if (OneUse) { + color = new Color(30, 22, 10); + } + + foreach (DreamSpinner spinner in Scene.Tracker.GetEntities()) { + if (spinner.OneUse == OneUse && spinner.ID > ID && (spinner.Position - Position).LengthSquared() < 576f) { + offsets.Add((Position + spinner.Position) / 2f); + } + } + } + + public override void Update() { + if (Fake) { + return; + } else if (!InView()) { + block.Active = false; + return; + } else { + block.Active = true; + } + + base.Update(); + + foreach (Actor actor in Scene.CollideAll(block.Collider.Bounds)) { + if (actor is CrystalDebris debris) { + debris.RemoveSelf(); + } + } + + Player player = GrabBagModule.playerInstance; + if (player != null) { + if (OneUse) { + bool isColliding = block.Collidable && player.Collider.Collide(block); + + if (!isColliding && hasCollided) { + RemoveSelf(); + block.RemoveSelf(); + + Audio.Play(SFX.game_06_fall_spike_smash, Position); + CrystalDebris.Burst(Center, debrisColor, false, 4); + return; + } + + hasCollided = isColliding; + } + + if ((player.DashAttacking || player.StateMachine.State == Player.StDreamDash) && player.Inventory.DreamDash) { + block.Collidable = true; + Collidable = false; + } else { + block.Collidable = false; + Collidable = true; + } + } + } + + public bool InView() { + Camera camera = (Scene as Level).Camera; + return X > camera.X - 16f && Y > camera.Y - 16f && X < camera.X + 320f + 16f && Y < camera.Y + 180f + 16f; + } + + private void OnPlayer(Player player) { + player.Die((player.Center - Center).SafeNormalize()); + } + } + + public class DreamSpinnerRenderer : Entity { + private const int ParticleCount = 630; // Particle count for a 320x180 Dream Block + private static readonly Vector2 origin = new Vector2(12f, 12f); + private static readonly BlendState DreamParticleBlend = new() { + ColorSourceBlend = Blend.DestinationAlpha, + ColorDestinationBlend = Blend.InverseSourceAlpha, + AlphaSourceBlend = Blend.Zero, + AlphaDestinationBlend = Blend.One, + AlphaBlendFunction = BlendFunction.Add + }; + + private readonly MTexture[] particleTextures; + private readonly MTexture fgSpinnerTexture; + private readonly MTexture bgSpinnerTexture; + private readonly MTexture fgBorderTexture; + private readonly MTexture bgBorderTexture; + + private VirtualRenderTarget dreamSpinnerTarget; + private List spinnersToRender; + private DreamParticle[] particles; + private bool dreamDashEnabled; + private float animTimer; + + public DreamSpinnerRenderer() { + Depth = -8500; + AddTag(Tags.Global | Tags.TransitionUpdate); + Add(new BeforeRenderHook(BeforeRender)); + + fgSpinnerTexture = GFX.Game["isafriend/danger/crystal/dreamSpinner"].GetSubtexture(0, 0, 24, 24); + bgSpinnerTexture = GFX.Game["isafriend/danger/crystal/dreamSpinner"].GetSubtexture(24, 0, 24, 24); + fgBorderTexture = GFX.Game["isafriend/danger/crystal/dreamBorder"].GetSubtexture(0, 0, 24, 24); + bgBorderTexture = GFX.Game["isafriend/danger/crystal/dreamBorder"].GetSubtexture(24, 0, 24, 24); + particleTextures = new MTexture[] { + GFX.Game["objects/dreamblock/particles"].GetSubtexture(14, 0, 7, 7), + GFX.Game["objects/dreamblock/particles"].GetSubtexture(7, 0, 7, 7), + GFX.Game["objects/dreamblock/particles"].GetSubtexture(0, 0, 7, 7), + GFX.Game["objects/dreamblock/particles"].GetSubtexture(7, 0, 7, 7) + }; + } + + public override void Awake(Scene scene) { + base.Awake(scene); + + dreamDashEnabled = SceneAs().Session.Inventory.DreamDash; + + Calc.PushRandom(0x12F3); // Chosen by Isa ¯\_(ツ)_/¯ + + particles = new DreamParticle[ParticleCount]; + for (int i = 0; i < particles.Length; i++) { + particles[i].Position = new Vector2(Calc.Random.NextFloat(320f), Calc.Random.NextFloat(180f)); + particles[i].Layer = Calc.Random.Choose(0, 1, 1, 2, 2, 2); + particles[i].TimeOffset = Calc.Random.NextFloat(); + particles[i].Color = particles[i].Layer switch { + 0 => Calc.Random.Choose(Calc.HexToColor("FFEF11"), Calc.HexToColor("FF00D0"), Calc.HexToColor("08a310")), + 1 => particles[i].Color = Calc.Random.Choose(Calc.HexToColor("5fcde4"), Calc.HexToColor("7fb25e"), Calc.HexToColor("E0564C")), + 2 => particles[i].Color = Calc.Random.Choose(Calc.HexToColor("5b6ee1"), Calc.HexToColor("CC3B3B"), Calc.HexToColor("7daa64")), + _ => Color.LightGray + }; + } + + Calc.PopRandom(); + } + + public override void Update() { + base.Update(); + animTimer += 6f * Engine.DeltaTime; + } + + public override void Render() { + if (spinnersToRender.Count > 0) { + Draw.SpriteBatch.Draw(dreamSpinnerTarget, SceneAs().Camera.Position, Color.White); + } + } + + public override void SceneEnd(Scene scene) { + base.SceneEnd(scene); + Dispose(); + } + + internal static void Load() { + On.Celeste.LevelLoader.LoadingThread += LevelLoader_LoadingThread; + } + + internal static void Unload() { + On.Celeste.LevelLoader.LoadingThread -= LevelLoader_LoadingThread; + } + + private static void LevelLoader_LoadingThread(On.Celeste.LevelLoader.orig_LoadingThread orig, LevelLoader self) { + self.Level.Add(new DreamSpinnerRenderer()); + orig(self); + } + + private void BeforeRender() { + spinnersToRender = GetSpinnersToRender(); + if (spinnersToRender.Count <= 0) { + return; + } + + Camera camera = SceneAs().Camera; + dreamDashEnabled = SceneAs().Session.Inventory.DreamDash; + + dreamSpinnerTarget ??= VirtualContent.CreateRenderTarget("dream-spinner-renderer", 320, 180); + + // First we draw our spinner textures and dream particles to a temp buffer + // We draw the particles with a special BlendState so they will only render over spinner textures + Engine.Graphics.GraphicsDevice.SetRenderTarget(GameplayBuffers.TempA); + Engine.Graphics.GraphicsDevice.Clear(Color.Transparent); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, null, camera.Matrix); + DrawSpinnerTextures(); + Draw.SpriteBatch.End(); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, DreamParticleBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, null, Matrix.Identity); + DrawDreamParticles(camera.Position); + Draw.SpriteBatch.End(); + + // We then switch to our main target, draw our borders, then draw the spinner textures + dream particles on top + Engine.Graphics.GraphicsDevice.SetRenderTarget(dreamSpinnerTarget); + Engine.Graphics.GraphicsDevice.Clear(Color.Transparent); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, null, camera.Matrix); + DrawSpinnerBorders(); + Draw.SpriteBatch.Draw(GameplayBuffers.TempA, camera.Position, Color.White); + Draw.SpriteBatch.End(); + } + + private void DrawSpinnerTextures() { + foreach (DreamSpinner spinner in spinnersToRender) { + fgSpinnerTexture.Draw(spinner.Position, origin, spinner.color, Vector2.One, spinner.rotation); + foreach (Vector2 bgOffset in spinner.offsets) { + bgSpinnerTexture.Draw(bgOffset, origin, spinner.color); + } + } + } + + private void DrawDreamParticles(Vector2 cameraPos) { + for (int i = 0; i < particles.Length; i++) { + int layer = particles[i].Layer; + Color color = dreamDashEnabled ? particles[i].Color : Color.LightGray * (0.5f + (layer / 2f * 0.5f)); + + MTexture texture = layer switch { + 0 => particleTextures[3 - (int)(((particles[i].TimeOffset * 4f) + animTimer) % 4f)], + 1 => particleTextures[1 + (int)(((particles[i].TimeOffset * 2f) + animTimer) % 2f)], + _ => particleTextures[2] + }; + + Vector2 vector = particles[i].Position - (cameraPos * (0.7f - (0.25f * layer))); + Vector2 position = new() { + X = Utils.Mod(vector.X, 320f), + Y = Utils.Mod(vector.Y, 180f) + }; + + texture.DrawCentered(position, color); + } + } + + private void DrawSpinnerBorders() { + foreach (DreamSpinner spinner in spinnersToRender) { + Color borderColor = !dreamDashEnabled ? Color.Gray : spinner.OneUse ? Color.Orange * 0.9f : Color.White; + fgBorderTexture.Draw(spinner.Position, origin, borderColor, Vector2.One, spinner.rotation); + foreach (Vector2 bgOffset in spinner.offsets) { + bgBorderTexture.Draw(bgOffset, origin, borderColor); + } + } + } + + private List GetSpinnersToRender() { + List spinnersToRender = new(); + foreach (DreamSpinner spinner in Scene.Tracker.GetEntities()) { + if (spinner.InView()) { + spinnersToRender.Add(spinner); + } + } + + return spinnersToRender; + } + + private void Dispose() { + if (dreamSpinnerTarget?.IsDisposed ?? false) { + dreamSpinnerTarget.Dispose(); + dreamSpinnerTarget = null; + } + } + + public struct DreamParticle { + public Vector2 Position; + public int Layer; + public Color Color; + public float TimeOffset; + } + } +} diff --git a/Code/ForceVariant.cs b/Code/ForceVariant.cs new file mode 100644 index 0000000..1fac4b9 --- /dev/null +++ b/Code/ForceVariant.cs @@ -0,0 +1,325 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Monocle; +using MonoMod.RuntimeDetour; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Celeste.Mod.IsaGrabBag { + public enum Variant { + Hiccups = 0, + InfiniteStamina = 1, + Invincible = 2, + InvisibleMotion = 3, + LowFriction = 4, + MirrorMode = 5, + NoGrabbing = 6, + PlayAsBadeline = 7, + SuperDashing = 8, + ThreeSixtyDashing = 9, + DashAssist = 10 + } + + public enum VariantState { + Enabled, + Disabled, + EnabledTemporary, + DisabledTemporary, + EnabledPermanent, + DisabledPermanent, + Toggle, + SetToDefault + } + + public static class ForceVariants { + private static readonly List menuLayout = new(){ + Variant.MirrorMode, + Variant.ThreeSixtyDashing, + Variant.InvisibleMotion, + Variant.NoGrabbing, + Variant.LowFriction, + Variant.SuperDashing, + Variant.Hiccups, + Variant.PlayAsBadeline, + Variant.InfiniteStamina, + Variant.DashAssist, + Variant.Invincible, + }; + + private static Dictionary itemList = new(); + private static Hook enableHook, disableHook, aPressHook; + private static TextMenu variantMenu; + + private static bool[] Variants_Default { get; set; } = new bool[] { false, false, false, false, false, false, false, false, false, false, false }; + private static bool?[] Variants { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null, null }; + + public static bool? GetVariantModdedValue(Variant variant) { + return Variants[(int)variant]; + } + + public static void GetDefaults() { + for (int i = 0; i < Variants_Default.Length; i++) { + Variants_Default[i] = GetVariantStatus((Variant)i); + } + } + + public static void SaveToSession() { + IsaSession session = GrabBagModule.Session; + for (int i = 0; i < Variants.Length; i++) { + session.Variants_Save[i] = Variants[i]; + } + } + + public static void GetFromSession() { + IsaSession session = GrabBagModule.Session; + for (int i = 0; i < Variants.Length; i++) { + if (Variants[i] != null && session.Variants_Save[i] == null) { + SetVariant(i, VariantState.SetToDefault); + } + + Variants[i] = session.Variants_Save[i]; + } + } + + public static void ReinforceSession() { + IsaSession session = GrabBagModule.Session; + for (int i = 0; i < 11; i++) { + Variants[i] = session.Variants_Save[i]; + if (session.Variants_Save[i] == null) { + continue; + } + + SetVariant(i, session.Variants_Save[i].Value ? VariantState.EnabledPermanent : VariantState.DisabledPermanent); + } + } + + public static void ResetSession() { + for (int i = 0; i < Variants_Default.Length; i++) { + SetVariant(i, VariantState.SetToDefault); + } + } + + public static void SetVariant(int variant, VariantState state) { + SetVariant((Variant)variant, state); + } + + public static void SetVariant(Variant variant, VariantState state) { + if (state == VariantState.SetToDefault) { + Variants[(int)variant] = null; + SetVariantInGame(variant, Variants_Default[(int)variant]); + return; + } + + bool permanent = state is VariantState.EnabledPermanent or VariantState.DisabledPermanent; + bool value = state == VariantState.Enabled || state == VariantState.EnabledTemporary || state == VariantState.EnabledPermanent || (state == VariantState.Toggle && !GetVariantStatus(variant)); + + SetVariantInGame(variant, value); + + if (permanent) { + Variants[(int)variant] = value; + } + } + + public static bool GetVariantStatus(Variant variant) { + return variant switch { + Variant.Hiccups => SaveData.Instance.Assists.Hiccups, + Variant.InfiniteStamina => SaveData.Instance.Assists.InfiniteStamina, + Variant.Invincible => SaveData.Instance.Assists.Invincible, + Variant.InvisibleMotion => SaveData.Instance.Assists.InvisibleMotion, + Variant.LowFriction => SaveData.Instance.Assists.LowFriction, + Variant.MirrorMode => SaveData.Instance.Assists.MirrorMode, + Variant.NoGrabbing => SaveData.Instance.Assists.NoGrabbing, + Variant.PlayAsBadeline => SaveData.Instance.Assists.PlayAsBadeline, + Variant.SuperDashing => SaveData.Instance.Assists.SuperDashing, + Variant.ThreeSixtyDashing => SaveData.Instance.Assists.ThreeSixtyDashing, + Variant.DashAssist => SaveData.Instance.Assists.DashAssist, + _ => false, + }; + } + + internal static void Load() { + On.Celeste.ChangeRespawnTrigger.OnEnter += OnChangeRespawn; + On.Celeste.Level.AssistMode += Level_AssistMode; + On.Celeste.Level.VariantMode += Level_VariantMode; + + Delegate optionChanged = new Action>, TextMenu.Option>(OnChange); + disableHook = new Hook( + typeof(TextMenu.Option).GetMethod("LeftPressed", BindingFlags.Instance | BindingFlags.Public), + optionChanged); + + enableHook = new Hook( + typeof(TextMenu.Option).GetMethod("RightPressed", BindingFlags.Instance | BindingFlags.Public), + optionChanged); + + aPressHook = new Hook( + typeof(TextMenu.Option).GetMethod("ConfirmPressed", BindingFlags.Instance | BindingFlags.Public), + optionChanged); + } + + internal static void Unload() { + On.Celeste.ChangeRespawnTrigger.OnEnter -= OnChangeRespawn; + On.Celeste.Level.AssistMode -= Level_AssistMode; + On.Celeste.Level.VariantMode -= Level_VariantMode; + On.Celeste.TextMenu.Close -= TextMenu_Close; + + enableHook?.Dispose(); + disableHook?.Dispose(); + aPressHook?.Dispose(); + enableHook = disableHook = aPressHook = null; + } + + private static void OnChangeRespawn(On.Celeste.ChangeRespawnTrigger.orig_OnEnter orig, ChangeRespawnTrigger self, Player player) { + orig(self, player); + SaveToSession(); + } + + private static void OnChange(Action> orig, TextMenu.Option self) { + orig(self); + + if (itemList.ContainsKey(self)) { + bool value = self.Index >= 1; + int index = itemList[self]; + Variants_Default[index] = value; + + } + } + + private static void Level_VariantMode(On.Celeste.Level.orig_VariantMode orig, Level self, int returnIndex, bool minimal) { + orig(self, returnIndex, minimal); + List list = self.Entities.ToAdd; + OnVariantMenu(list[list.Count - 1] as TextMenu, false); + + } + private static void Level_AssistMode(On.Celeste.Level.orig_AssistMode orig, Level self, int returnIndex, bool minimal) { + orig(self, returnIndex, minimal); + List list = self.Entities.ToAdd; + OnVariantMenu(list[list.Count - 1] as TextMenu, true); + } + private static bool bingoUIMenuModified => (GrabBagModule.BingoUIModuleSettings?.Get("Enabled") ?? false) && GrabBagModule.BingoUIModuleSettings.Get("HideVariantsExceptGrabless"); + private static void OnVariantMenu(TextMenu menu, bool assist) { + variantMenu = menu; + IsaSession session = GrabBagModule.Session; + itemList = new Dictionary(); + + On.Celeste.TextMenu.Close += TextMenu_Close; + + int index = assist ? 8 : 0; + for (int i = 0; i < menu.Items.Count; ++i) { + TextMenu.Item item = menu.Items[i]; + if (item is not TextMenu.OnOff) { + continue; + } + + Variant v = (!assist && bingoUIMenuModified) ? Variant.NoGrabbing : menuLayout[index++]; + itemList.Add(item, (int)v); + } + } + + private static void TextMenu_Close(On.Celeste.TextMenu.orig_Close orig, TextMenu self) { + orig(self); + if (variantMenu != self) { + return; + } + + On.Celeste.TextMenu.Close -= TextMenu_Close; + } + + private static void SetVariantInGame(Variant variant, bool value) { + // Set value in game + switch (variant) { + case Variant.Hiccups: + SaveData.Instance.Assists.Hiccups = value; + break; + case Variant.InfiniteStamina: + SaveData.Instance.Assists.InfiniteStamina = value; + break; + case Variant.Invincible: + SaveData.Instance.Assists.Invincible = value; + break; + case Variant.InvisibleMotion: + SaveData.Instance.Assists.InvisibleMotion = value; + break; + case Variant.LowFriction: + SaveData.Instance.Assists.LowFriction = value; + break; + case Variant.MirrorMode: + SaveData.Instance.Assists.MirrorMode = Input.Aim.InvertedX = Input.MoveX.Inverted = value; + break; + case Variant.NoGrabbing: + SaveData.Instance.Assists.NoGrabbing = value; + break; + case Variant.PlayAsBadeline: + bool originalValue = SaveData.Instance.Assists.PlayAsBadeline; + SaveData.Instance.Assists.PlayAsBadeline = value; + if (value != originalValue) { + // apply the effect immediately + Player entity = Engine.Scene.Tracker.GetEntity(); + if (entity != null) { + PlayerSpriteMode mode = SaveData.Instance.Assists.PlayAsBadeline ? PlayerSpriteMode.MadelineAsBadeline : entity.DefaultSpriteMode; + if (entity.Active) { + entity.ResetSpriteNextFrame(mode); + return; + } + + entity.ResetSprite(mode); + } + } + + break; + case Variant.SuperDashing: + SaveData.Instance.Assists.SuperDashing = value; + break; + case Variant.ThreeSixtyDashing: + SaveData.Instance.Assists.ThreeSixtyDashing = value; + break; + case Variant.DashAssist: + SaveData.Instance.Assists.DashAssist = value; + break; + } + } + } + + [CustomEntity("ForceVariantTrigger")] + public class ForceVariantTrigger : Trigger { + public Variant variant; + public VariantState enable; + + private bool previous; + + public ForceVariantTrigger(EntityData data, Vector2 offset) : base(data, offset) { + variant = data.Enum("variantChange", Variant.Hiccups); + enable = data.Enum("enableStyle", VariantState.Enabled); + + if (data.Has("enforceValue")) { + bool enforce = data.Bool("enforceValue"); + if (enable == VariantState.Enabled) { + enable = VariantState.EnabledTemporary; + } else if (enable == VariantState.Disabled) { + enable = VariantState.DisabledTemporary; + } else if (enable == VariantState.EnabledPermanent && !enforce) { + enable = VariantState.Enabled; + } else if (enable == VariantState.DisabledPermanent && !enforce) { + enable = VariantState.Disabled; + } + } + } + + public override void OnEnter(Player player) { + base.OnEnter(player); + previous = ForceVariants.GetVariantStatus(variant); + ForceVariants.SetVariant(variant, enable); + } + + public override void OnLeave(Player player) { + base.OnLeave(player); + + switch (enable) { + case VariantState.DisabledTemporary: + case VariantState.EnabledTemporary: + ForceVariants.SetVariant(variant, previous ? VariantState.Enabled : VariantState.Disabled); + break; + } + } + } +} diff --git a/Code/GrabBagMeta.cs b/Code/GrabBagMeta.cs new file mode 100644 index 0000000..6d9bb9e --- /dev/null +++ b/Code/GrabBagMeta.cs @@ -0,0 +1,19 @@ +using Celeste.Mod.Meta; + +namespace Celeste.Mod.IsaGrabBag { + public class GrabBagWrapperMeta : IMeta { + public GrabBagMeta IsaGrabBag { get; set; } + } + + public class GrabBagMeta { + public bool WaterBoost { get; set; } + public bool RoundDreamSpinner { get; set; } + + public static GrabBagMeta Default(AreaKey area) { + return new GrabBagMeta() { + WaterBoost = area.LevelSet.StartsWith("SpringCollab2020"), + RoundDreamSpinner = false + }; + } + } +} diff --git a/Code/IsaGrabBag.csproj b/Code/IsaGrabBag.csproj new file mode 100644 index 0000000..f4eba55 --- /dev/null +++ b/Code/IsaGrabBag.csproj @@ -0,0 +1,60 @@ + + + + net452 + IsaMods + Celeste.Mod.IsaGrabBag + latest + ..\.. + ..\..\.. + lib-stripped + XNA + FNA + $(WINDIR)\Microsoft.NET\assembly\GAC_32\{0}\v4.0_4.0.0.0__842cf8be1de50553\{0}.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(MSBuildProjectDirectory)=IsaGrabBag/ + + + + None + + + diff --git a/Code/Module.cs b/Code/Module.cs new file mode 100644 index 0000000..274816b --- /dev/null +++ b/Code/Module.cs @@ -0,0 +1,184 @@ +using Microsoft.Xna.Framework; +using Monocle; +using System; + +namespace Celeste.Mod.IsaGrabBag { + public class GrabBagModule : EverestModule { + public GrabBagModule() { + Instance = this; + } + + private static GrabBagMeta gbMeta; + public static GrabBagMeta GrabBagMeta { + get { + if (gbMeta == null) { + + AreaKey key; + Level level = Engine.Scene as Level; + level ??= Engine.NextScene as Level; + + if (level != null) { + key = level.Session.Area; + } else { + LevelLoader loader = Engine.Scene as LevelLoader; + key = loader.Level.Session.Area; + } + + gbMeta = GrabBagMeta.Default(key); + }; + return gbMeta; + } + } + + public override Type SessionType => typeof(IsaSession); + public static IsaSession Session => (IsaSession)Instance._Session; + + public static GrabBagModule Instance { get; private set; } + + public static Player playerInstance { get; private set; } + + public static SpriteBank sprites { get; private set; } + + public static MonoMod.Utils.DynamicData BingoUIModuleSettings; + + public override void Load() { + ArrowBubble.Load(); + BadelineFollower.Load(); + DreamSpinnerRenderer.Load(); + ForceVariants.Load(); + RewindCrystal.Load(); + ZipLine.Load(); + + Everest.Events.Level.OnTransitionTo += Level_OnTransitionTo; + Everest.Events.Level.OnEnter += Level_OnEnter; + Everest.Events.Level.OnExit += Level_OnExit; + Everest.Events.LevelLoader.OnLoadingThread += LevelLoader_OnLoadingThread; + Everest.Events.Player.OnSpawn += Player_OnSpawn; + + if (Everest.Loader.TryGetDependency(new() { Name = "BingoUI", Version = new(1, 2, 6) }, out var BingoUIModule)) + BingoUIModuleSettings = MonoMod.Utils.DynamicData.For(BingoUIModule._Settings); + } + + public override void Unload() { + ArrowBubble.Unload(); + BadelineFollower.Unload(); + DreamSpinnerRenderer.Unload(); + ForceVariants.Unload(); + RewindCrystal.Unload(); + ZipLine.Unload(); + + Everest.Events.Level.OnTransitionTo -= Level_OnTransitionTo; + Everest.Events.Level.OnEnter -= Level_OnEnter; + Everest.Events.Level.OnExit -= Level_OnExit; + Everest.Events.LevelLoader.OnLoadingThread -= LevelLoader_OnLoadingThread; + Everest.Events.Player.OnSpawn -= Player_OnSpawn; + } + + public override void LoadContent(bool firstLoad) { + RewindCrystal.LoadGraphics(); + sprites = new SpriteBank(GFX.Game, "Graphics/IsaGrabBag.xml"); + } + + private void Player_OnSpawn(Player player) { + Level lvl = player.SceneAs(); + + playerInstance = player; + + ForceVariants.GetFromSession(); + + if (player.Get() == null) { + player.Add(new WaterBoostHandler()); + } + + if (lvl.Session.GetFlag(BadelineFollower.IsaGrabBag_HasBadelineFollower)) { + foreach (BadelineBoost boost in lvl.Entities.FindAll()) { + boost.Visible = false; + boost.Collidable = false; + } + + if (lvl.Entities.FindFirst() == null) { + if (BadelineFollower.instance == null) { + BadelineFollower follower = new(lvl, player.Position + new Vector2((int)playerInstance.Facing * -12, -20)); + lvl.Add(follower); + player.Leader.GainFollower(follower.follower); + } else { + BadelineFollower.instance.Readd(lvl, player); + } + } + } + + if (lvl.Session.GetFlag(BadelineFollower.IsaGrabBag_HasBadelineFollower)) { + BadelineFollower.instance.dummy.Visible = true; + } + + BadelineFollower.CheckBooster(lvl, false); + } + + private void Level_OnExit(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) { + gbMeta = null; + + ForceVariants.ResetSession(); + + if (BadelineFollower.instance != null) { + BadelineFollower.instance.RemoveSelf(); + } + + BadelineFollower.instance = null; + } + + private void LevelLoader_OnLoadingThread(Level level) { + Session session = level.Session; + + try { + gbMeta = null; + + //string s = session.Area.GetSID(); + if (session != null && session.Area != null && session.Area.SID != null) { + + ModAsset get = Everest.Content.Get(session.Area.SID); + if (get != null && get.TryGetMeta(out GrabBagWrapperMeta parsed)) { + gbMeta = parsed.IsaGrabBag; + } + } + } catch (Exception e) { + Logger.Log(LogLevel.Warn, "IsaGrabBag", "Unable to properly get metadata"); + Logger.LogDetailed(e); + } + + gbMeta ??= GrabBagMeta.Default(session.Area); + + if (session.Area.LevelSet.StartsWith("SpringCollab2020")) { + GrabBagMeta.WaterBoost = true; + } + } + + private void Level_OnEnter(Session session, bool fromSaveData) { + ForceVariants.GetDefaults(); + ForceVariants.ReinforceSession(); + } + + private void Level_OnTransitionTo(Level level, LevelData next, Vector2 direction) { + ForceVariants.SaveToSession(); + + if (GrabBagMeta == null) { + gbMeta = GrabBagMeta.Default(level.Session.Area); + } + + if (level.Session.GetFlag(BadelineFollower.IsaGrabBag_HasBadelineFollower)) { + if (BadelineFollower.instance == null) { + BadelineFollower follower = new(level, playerInstance.Position); + level.Add(follower); + playerInstance.Leader.GainFollower(follower.follower); + + BadelineFollower.CheckBooster(level, false); + } + + BadelineFollower.Search(); + + if (level.Session.GetFlag(BadelineFollower.IsaGrabBag_HasBadelineFollower)) { + BadelineFollower.instance.dummy.Visible = true; + } + } + } + } +} diff --git a/Code/RewindCrystal.cs b/Code/RewindCrystal.cs new file mode 100644 index 0000000..4087c02 --- /dev/null +++ b/Code/RewindCrystal.cs @@ -0,0 +1,376 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Monocle; +using System; +using System.Collections; + +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/rewindCrystal")] + public class RewindCrystal : Entity { + private const float RewindCount = 3.5f; + private const int rewindFrames = (int)(RewindCount * 60); + + private static readonly CharacterState[] states = new CharacterState[rewindFrames]; + private readonly bool oneUse = false; + private static float RenderStrength; + private static int currentFrame; + private static int maxRewind; + private static RenderTarget2D playerTarget; + private static Effect glitchEffect; + + private readonly Sprite visuals; + private readonly Image outline; + private Level level; + + public RewindCrystal(Vector2 position) + : base(position) { + visuals = GrabBagModule.sprites.Create("rewind_crystal"); + outline = new Image(GFX.Game["isafriend/objects/rewind/outline00"]) { + Position = new Vector2(-8, -8), + Visible = false + }; + + Collider = new Hitbox(16, 16, -8, -8); + + Add(visuals); + Add(new PlayerCollider(OnPlayer)); + Add(outline); + + Depth = 1500; + } + + public RewindCrystal(EntityData data, Vector2 offset) + : this(data.Position + offset) { + } + + public static bool Rewinding { get; internal set; } = false; + + public static void ClearRewindBuffer() { + currentFrame = 0; + maxRewind = 0; + states[states.Length - 1] = new CharacterState(); + } + + public static void Load() { + On.Celeste.PlayerCollider.Check += PlayerCollider_Check; + On.Celeste.Player.Update += Player_Update; + On.Celeste.Player.Render += Player_Render; + Everest.Events.Level.OnLoadLevel += Level_OnLoadLevel; + On.Celeste.Mod.UI.SubHudRenderer.Render += SubHudRenderer_Render; + } + + public static void Unload() { + On.Celeste.PlayerCollider.Check -= PlayerCollider_Check; + On.Celeste.Player.Update -= Player_Update; + On.Celeste.Player.Render -= Player_Render; + Everest.Events.Level.OnLoadLevel -= Level_OnLoadLevel; + On.Celeste.Mod.UI.SubHudRenderer.Render -= SubHudRenderer_Render; + } + + public static void LoadGraphics() { + playerTarget = new RenderTarget2D(Draw.SpriteBatch.GraphicsDevice, 64, 64); + + try { + ModAsset asset = Everest.Content.Get("Effects/glitchy_effect.cso"); + glitchEffect = new Effect(Draw.SpriteBatch.GraphicsDevice, asset.Data); + } catch (Exception e) { + Logger.Log(LogLevel.Error, "IsaGrabBag", "Failed to load the shader"); + Logger.LogDetailed(e); + } + } + + public override void Added(Scene scene) { + base.Added(scene); + level = SceneAs(); + } + + private static void Player_Render(On.Celeste.Player.orig_Render orig, Player self) { + if (RenderStrength > 0) { + GameplayRenderer.End(); + + Draw.SpriteBatch.GraphicsDevice.SetRenderTarget(playerTarget); + Draw.SpriteBatch.GraphicsDevice.Clear(Color.Transparent); + + Matrix m = Matrix.CreateTranslation(-(self.X - 32), -(self.Y - 32), 0); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, null, m); + + orig(self); + + Draw.SpriteBatch.End(); + + Draw.SpriteBatch.GraphicsDevice.SetRenderTarget(GameplayBuffers.Gameplay); + GameplayRenderer.Begin(); + } else { + orig(self); + + } + } + + private static void SubHudRenderer_Render(On.Celeste.Mod.UI.SubHudRenderer.orig_Render orig, UI.SubHudRenderer self, Scene scene) { + if (MInput.Keyboard.Pressed(Microsoft.Xna.Framework.Input.Keys.Q)) { + LoadGraphics(); + } + + if (scene is Level && RenderStrength > 0) { + Level level = scene as Level; + + Entity e = level.Entities.FindFirst(); + + if (e == null) { + orig(self, scene); + return; + } + + float scale = Math.Min(Engine.ViewWidth / 320.0f, Engine.ViewHeight / 180.0f); + + Vector2 p = e.Position; + + Matrix m = Matrix.CreateTranslation(p.X - 32, p.Y - 32, 0) * level.Camera.Matrix * Matrix.CreateScale(scale); + + if (RenderStrength > 0.1) { + + glitchEffect.Parameters["time"].SetValue(scene.RawTimeActive); + glitchEffect.Parameters["strength"].SetValue(1); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, glitchEffect, m); + + } else { + + ColorGrade.Set(GFX.ColorGrades["isagrabbag/rewind_flash"]); + + Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, ColorGrade.Effect, m); + + } + + Draw.SpriteBatch.Draw(playerTarget, Vector2.Zero, Color.White); + + Draw.SpriteBatch.End(); + } + + orig(self, scene); + } + + private static bool PlayerCollider_Check(On.Celeste.PlayerCollider.orig_Check orig, PlayerCollider self, Player player) { + return !Rewinding && orig(self, player); + } + + private static void Level_OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader) { + ClearRewindBuffer(); + Rewinding = false; + } + + private static void Player_Update(On.Celeste.Player.orig_Update orig, Player self) { + if (Rewinding) { + Engine.TimeRate = 1; + orig(self); + Engine.TimeRate = 0; + } else { + orig(self); + states[currentFrame] = new CharacterState(self); + currentFrame++; + currentFrame %= states.Length; + + if (maxRewind < states.Length) { + maxRewind++; + } + } + } + + private void OnPlayer(Player player) { + if (Rewinding) { + return; + } + + Coroutine co = new(RewindRoutine()) { + UseRawDeltaTime = true + }; + Audio.Play("event:/new_content/game/10_farewell/pinkdiamond_touch", Position); + + Add(co); + } + + private IEnumerator RewindRoutine() { + + Player player = Scene.Tracker.GetEntity(); + Rewinding = true; + player.Collidable = false; + + Celeste.Freeze(0.05f); + Collidable = false; + yield return null; + + visuals.Visible = false; + if (!oneUse) { + outline.Visible = true; + } + + level.Shake(); + + player.StateMachine.State = Player.StDummy; + player.ForceCameraUpdate = true; + player.DummyGravity = false; + + Engine.TimeRate = 0; + + float f; + for (f = 0; f < 0.5f; f += Engine.RawDeltaTime) { + + RenderStrength = f * 2; + + if (Input.MenuConfirm.Pressed) { + Input.MenuConfirm.ConsumePress(); + break; + } + + yield return null; + } + + RenderStrength = 1; + + int stateLen = states.Length; + int lastFrame = currentFrame; + + int unpausedState = (currentFrame + 1) % stateLen; + bool hitPause = false; + + const float rewindLength = 2; + + for (f = 0; f < rewindLength + .15f; f += Engine.RawDeltaTime) { + + if (f < rewindLength && !hitPause) { + + float sampledCurve = 1 - Ease.SineInOut(f / 2); + + int offset = (int)((stateLen - 1) * sampledCurve); + int newFrame = (currentFrame + offset) % stateLen; + + while (lastFrame != newFrame) { + + lastFrame = (lastFrame + stateLen - 1) % stateLen; + + if (lastFrame > currentFrame && maxRewind < stateLen) { + unpausedState = 0; + break; + } + + states[lastFrame].SetOnPlayer(player); + + if (level.CollideCheck(player.Collider.Bounds)) { + unpausedState = lastFrame; + hitPause = true; + f = rewindLength - 0.15f; + break; + } + } + } + + if (f >= rewindLength - 0.15f) { + RenderStrength = Calc.ClampedMap(f, rewindLength - 0.15f, rewindLength + 0.15f, 1, 0);// 1 - ((f - rewindLength) / .15f); + } + + Vector2 position = level.Camera.Position; + Vector2 cameraTarget = player.CameraTarget; + + level.Camera.Position = position + ((cameraTarget - position) * (1f - (float)Math.Pow(0.01f, 0.1))); + + yield return null; + } + + RenderStrength = 0; + states[unpausedState].SetOnPlayerFinal(player); + + yield return 0.1f; + + player.StateMachine.State = Player.StNormal; + Engine.TimeRate = 1; + + Rewinding = false; + + player.Collidable = true; + + maxRewind = 0; + + ClearRewindBuffer(); + if (oneUse) { + RemoveSelf(); + } else { + Add(new Coroutine(RespawnRoutine())); + } + } + + private IEnumerator RespawnRoutine() { + + yield return 1.5f; + + visuals.Visible = true; + Collidable = true; + Audio.Play("event:/game/general/diamond_return", Position); + } + } + + [CustomEntity("isaBag/pauseCrystal")] + [Tracked(false)] + public class UnpauseCrystal : Entity { + private readonly Sprite visuals; + + public UnpauseCrystal(Vector2 position) : base(position) { + visuals = GrabBagModule.sprites.Create("pause_crystal"); + Collider = new Hitbox(12, 12, -6, -6); + Add(visuals); + Depth = 1500; + } + + public UnpauseCrystal(EntityData data, Vector2 offset) + : this(data.Position + offset) { + } + } + + internal struct CharacterState { + public Vector2 pos, scale; + public Facings facing; + public int dashCount; + public Color hairColor; + public Vector2[] hairPosition; + public MTexture texture; + public Vector2 velocity; + + public CharacterState(Player player) { + pos = player.Position; + scale = player.Sprite.Scale; + facing = player.Facing; + texture = player.Sprite.Texture; + dashCount = player.Dashes; + velocity = player.Speed; + PlayerHair hair = player.Hair; + hairColor = hair.Color; + hairPosition = new Vector2[10]; + + for (int i = 0; i < Math.Min(hair.Nodes.Count, hairPosition.Length); ++i) { + hairPosition[i] = hair.Nodes[i]; + } + } + + public void SetOnPlayer(Player player) { + player.NaiveMove(pos - player.Position); + player.Sprite.Scale = scale; + player.Facing = facing; + player.Dashes = dashCount; + + player.Sprite.Texture = texture; + + if (hairPosition == null) { + return; + } + + for (int i = 0; i < Math.Min(player.Hair.Nodes.Count, hairPosition.Length); ++i) { + player.Hair.Nodes[i] = hairPosition[i]; + } + } + public void SetOnPlayerFinal(Player player) { + SetOnPlayer(player); + player.Speed = velocity; + } + } +} diff --git a/Code/Session.cs b/Code/Session.cs new file mode 100644 index 0000000..a763aa7 --- /dev/null +++ b/Code/Session.cs @@ -0,0 +1,5 @@ +namespace Celeste.Mod.IsaGrabBag { + public class IsaSession : EverestModuleSession { + public bool?[] Variants_Save { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null, null }; + } +} \ No newline at end of file diff --git a/Code/Shaders/dream_border.fx b/Code/Shaders/dream_border.fx new file mode 100644 index 0000000..b6e254c --- /dev/null +++ b/Code/Shaders/dream_border.fx @@ -0,0 +1,50 @@ +// Pixel shader combines the bloom image with the original +// scene, using tweakable intensity levels and saturation. +// This is the final step in applying a bloom postprocess. + +sampler screen : register(s0); + +float size; + +float4 PixelShaderF(float2 texCoord : TEXCOORD0, float4 color : COLOR0) : COLOR0 +{ + + float4 b = tex2D(screen, texCoord); + + if (b.a == 0){ + + float2 offset = float2(1 / 362.0, 1 / 182.0); + + b = tex2D(screen, texCoord + float2(offset[0], 0)); + if (b.a == 1) + return color; + b = tex2D(screen, texCoord - float2(offset[0], 0)); + if (b.a == 1) + return color; + b = tex2D(screen, texCoord + float2(0, offset[0])); + if (b.a == 1) + return color; + b = tex2D(screen, texCoord - float2(0, offset[0])); + if (b.a == 1) + return color; + + return 0; + } + + return b; +} + + +technique BloomCombine +{ + pass Pass1 + { +#if SM4 + PixelShader = compile ps_4_0_level_9_1 PixelShaderF(); +#elif SM3 + PixelShader = compile ps_3_0 PixelShaderF(); +#else + PixelShader = compile ps_2_0 PixelShaderF(); +#endif + } +} diff --git a/Code/Shaders/dream_spinners.fx b/Code/Shaders/dream_spinners.fx new file mode 100644 index 0000000..e7b2c2a --- /dev/null +++ b/Code/Shaders/dream_spinners.fx @@ -0,0 +1,64 @@ +// Pixel shader combines the bloom image with the original +// scene, using tweakable intensity levels and saturation. +// This is the final step in applying a bloom postprocess. + +sampler screen : register(s0); + +struct VertexInput { + float4 Color : COLOR0; + float2 UVMapping : TEXCOORD0; + float4 Position : SV_Position; +}; +struct PixelInput +{ + float4 Color : COLOR0; + float2 UVMapping : TEXCOORD0; + float2 Position : TEXCOORD1; +}; +struct PixelOutput +{ + float4 Color : COLOR0; + float Depth : SV_Depth; +}; + +float2 pos; + +PixelInput VertexShaderF(VertexInput input) +{ + PixelInput output; + + output.Color = input.Color; + output.UVMapping = input.UVMapping; + output.Position = float2(1, 1); + + return output; +} +PixelOutput PixelShaderF(PixelInput input) +{ + PixelOutput output; + + output.Color = tex2D(screen, input.UVMapping) * input.Color; + if (output.Color.a == 0){ + output.Depth = 0; + } + else{ + output.Depth = 1; + } + + return output; +} + + +technique BloomCombine +{ + pass Pass1 + { +#if SM4 + PixelShader = compile ps_4_0_level_9_1 PixelShaderF(); +#elif SM3 + PixelShader = compile ps_3_0 PixelShaderF(); +#else + PixelShader = compile ps_2_0 PixelShaderF(); +#endif + } +} diff --git a/Code/Shaders/dream_stars.fx b/Code/Shaders/dream_stars.fx new file mode 100644 index 0000000..6b71d7a --- /dev/null +++ b/Code/Shaders/dream_stars.fx @@ -0,0 +1,59 @@ +// Pixel shader combines the bloom image with the original +// scene, using tweakable intensity levels and saturation. +// This is the final step in applying a bloom postprocess. + +sampler screen : register(s0); + +struct VertexInput { + float4 Color : COLOR0; + float2 UVMapping : TEXCOORD0; + float4 Position : SV_Position; +}; +struct PixelInput +{ + float4 Color : COLOR0; + float2 UVMapping : TEXCOORD0; + float2 Position : TEXCOORD1; +}; +struct PixelOutput +{ + float4 Color : COLOR0; + float Depth : SV_Depth; +}; + +float2 pos; + +PixelInput VertexShaderF(VertexInput input) +{ + PixelInput output; + + output.Color = input.Color; + output.UVMapping = input.UVMapping; + output.Position = float2(1, 1); + + return output; +} +PixelOutput PixelShaderF(PixelInput input) +{ + PixelOutput output; + + output.Color = tex2D(screen, input.UVMapping) * input.Color; + output.Depth = 1; + + return output; +} + + +technique BloomCombine +{ + pass Pass1 + { +#if SM4 + PixelShader = compile ps_4_0_level_9_1 PixelShaderF(); +#elif SM3 + PixelShader = compile ps_3_0 PixelShaderF(); +#else + PixelShader = compile ps_2_0 PixelShaderF(); +#endif + } +} diff --git a/Code/Shaders/glitchy_effect.fx b/Code/Shaders/glitchy_effect.fx new file mode 100644 index 0000000..fcfeb01 --- /dev/null +++ b/Code/Shaders/glitchy_effect.fx @@ -0,0 +1,79 @@ +// Pixel shader combines the bloom image with the original +// scene, using tweakable intensity levels and saturation. +// This is the final step in applying a bloom postprocess. + +texture spriteTexture; +sampler2D spriteSampler = sampler_state { + Texture = (spriteTexture); + MagFilter = Linear; + MinFilter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +float time, strength; + +struct VertexInput { + float4 Position : POSITION0; + float2 UVMapping : TEXCOORD0; +}; +struct VertexOutput { + float4 Position : POSITION0; + float2 UVMapping : TEXCOORD0; +}; + +float4 getframe(float2 uv){ + + float4 color = tex2D(spriteSampler, uv); + + return color; +} + +float rand1(float co){ + return frac(sin(dot(float2(1, co), float2(12.9898, 78.233))) * 43758.5453); +} + +float4 PixelShaderF(VertexOutput input) : COLOR0 +{ + float2 uv = input.UVMapping; + + float yPos = uv[1] * 64; + + float factor = yPos % 1; + yPos = floor(yPos); + factor = abs(factor * 2 - 1); + + uv[0] += (rand1(yPos + time) - 0.5f) * 0.03 * strength; + + yPos /= 64; + + float alpha = getframe(uv)[3]; + + float4 red = getframe(uv + float2(0.025 * strength, 0.0)); + float4 green = getframe(uv + float2(0, 0.0)); + float4 blue = getframe(uv + float2(-0.025 * strength, 0.0)); + + red = float4(red[0], green[1], blue[2], 0); + + red = lerp(red, 0, factor * 0.5 * strength); + red = lerp(red, red * rand1(yPos + time), 0.4 * strength) * 1.3; + + red[3] = alpha; + + return red; +} + + +technique BloomCombine +{ + pass Pass1 + { +#if SM4 + PixelShader = compile ps_4_0_level_9_1 PixelShaderF(); +#elif SM3 + PixelShader = compile ps_3_0 PixelShaderF(); +#else + PixelShader = compile ps_2_0 PixelShaderF(); +#endif + } +} \ No newline at end of file diff --git a/StateMachineEXT.cs b/Code/StateMachineEXT.cs similarity index 67% rename from StateMachineEXT.cs rename to Code/StateMachineEXT.cs index 5994b57..0cace8a 100644 --- a/StateMachineEXT.cs +++ b/Code/StateMachineEXT.cs @@ -3,16 +3,18 @@ using System.Collections; using System.Reflection; -namespace Celeste.Mod.IsaGrabBag -{ - internal static class StateMachineExt - { +namespace Celeste.Mod.IsaGrabBag { + internal static class StateMachineExt { + private static readonly FieldInfo StateMachine_begins = typeof(StateMachine).GetField("begins", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly FieldInfo StateMachine_updates = typeof(StateMachine).GetField("updates", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly FieldInfo StateMachine_ends = typeof(StateMachine).GetField("ends", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly FieldInfo StateMachine_coroutines = typeof(StateMachine).GetField("coroutines", BindingFlags.Instance | BindingFlags.NonPublic); + /// /// Adds a state to a StateMachine. Ripped straight from JaThePlayer lol /// /// The index of the new state - public static int AddState(this StateMachine machine, Func onUpdate, Func coroutine = null, Action begin = null, Action end = null) - { + public static int AddState(this StateMachine machine, Func onUpdate, Func coroutine = null, Action begin = null, Action end = null) { Action[] begins = (Action[])StateMachine_begins.GetValue(machine); Func[] updates = (Func[])StateMachine_updates.GetValue(machine); Action[] ends = (Action[])StateMachine_ends.GetValue(machine); @@ -32,9 +34,5 @@ public static int AddState(this StateMachine machine, Func onUpdate, Func(posOffset) || player.CollideCheck(posOffset + (Vector2.UnitY * -8f)); + if (!isInWater && player.StateMachine.State == Player.StSwim && (player.Speed.Y < 0 || Input.MoveY.Value == -1 || Input.Jump.Check)) { + player.Speed.Y = (Input.MoveY.Value == -1 || Input.Jump.Check) ? -110 : 0; + if (player.Speed.Y < -1) { + player.Speed.X *= 1.1f; + } + } + } + } + } +} diff --git a/Code/WindTrigger.cs b/Code/WindTrigger.cs new file mode 100644 index 0000000..48dc15f --- /dev/null +++ b/Code/WindTrigger.cs @@ -0,0 +1,43 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; + +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/coreWindTrigger", "CoreHeatWindTrigger")] + public class CoreWindTrigger : Trigger { + public WindController.Patterns windHot, windCold; + private static bool current; + + public CoreWindTrigger(EntityData data, Vector2 offset) : base(data, offset) { + windHot = data.Enum("patternHot", WindController.Patterns.Up); + windCold = data.Enum("patternCold", WindController.Patterns.Down); + } + + public override void OnEnter(Player player) { + base.OnEnter(player); + + WindController.Patterns currentWind = current ? windHot : windCold; + SetWind(Scene, currentWind); + } + public override void OnStay(Player player) { + base.OnStay(player); + + if (SceneAs().CoreMode == Session.CoreModes.Hot != current) { + current = !current; + WindController.Patterns currentWind = current ? windHot : windCold; + SetWind(Scene, currentWind); + } + } + + public static void SetWind(Monocle.Scene scene, WindController.Patterns currentWind) { + WindController wind = scene.Entities.FindFirst(); + if (wind == null) { + wind = new WindController(currentWind); + scene.Add(wind); + return; + } + + wind.SetPattern(WindController.Patterns.None); + wind.SetPattern(currentWind); + } + } +} diff --git a/Code/ZipLine.cs b/Code/ZipLine.cs new file mode 100644 index 0000000..aac9b45 --- /dev/null +++ b/Code/ZipLine.cs @@ -0,0 +1,346 @@ +using Celeste.Mod.Entities; +using Microsoft.Xna.Framework; +using Monocle; +using MonoMod.Utils; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Celeste.Mod.IsaGrabBag { + [CustomEntity("isaBag/zipline")] + public class ZipLine : Entity { + public const string NeverUsedZiplineFlag = "IsaGrabBag_NeverUsedZipline"; + + private const float ZIP_SPEED = 120f; + private const float ZIP_ACCEL = 190f; + private const float ZIP_TURN = 250f; + + private static ZipLine currentGrabbed, lastGrabbed; + private static float ziplineBuffer; + + private readonly float height; + private readonly Sprite sprite; + private readonly bool usesStamina; + private float speed; + private bool grabbed; + + public ZipLine(EntityData _data, Vector2 offset) + : base(_data.Position + offset) { + LeftEdge = X; + RightEdge = X; + foreach (Vector2 node in _data.Nodes) { + LeftEdge = Math.Min(node.X + offset.X, LeftEdge); + RightEdge = Math.Max(node.X + offset.X, RightEdge); + } + + usesStamina = _data.Bool("usesStamina", true); + height = (_data.Position + offset).Y; + Collider = new Hitbox(20, 16, -10, 1); + currentGrabbed = null; + Depth = -500; + + sprite = GrabBagModule.sprites.Create("zipline"); + sprite.Play("idle"); + sprite.JustifyOrigin(new Vector2(0.5f, 0.25f)); + Add(sprite); + } + + public static int ZipLineState { get; private set; } + public static bool GrabbingCoroutine => currentGrabbed != null && !currentGrabbed.grabbed; + public float LeftEdge { get; } + public float RightEdge { get; } + + public override void Added(Scene scene) { + base.Added(scene); + scene.Add(new ZipLineRender(this)); + } + + public override void Update() { + base.Update(); + Player player = GrabBagModule.playerInstance; + + if (player == null || player.Dead) { + return; + } + + if (grabbed) { + if (Math.Abs(player.Speed.X) > 20) { + player.LiftSpeed = player.Speed; + player.LiftSpeedGraceTime = 0.2f; + } + + if (player.CenterX > RightEdge || player.CenterX < LeftEdge) { + player.Speed.X = 0; + } + + player.CenterX = MathHelper.Clamp(player.CenterX, LeftEdge, RightEdge); + Position.X = player.CenterX; + Position.Y = height; + } else { + if (currentGrabbed == null && player != null && !player.Dead && player.CanUnDuck && Input.GrabCheck && CanGrabZip(this)) { + bool isTired = DynamicData.For(player).Get("IsTired"); + if (player.CollideCheck(this) && !isTired) { + currentGrabbed = this; + lastGrabbed = currentGrabbed; + player.StateMachine.State = ZipLineState; + } + } + + Position.X += speed * Engine.DeltaTime; + Position.X = MathHelper.Clamp(Position.X, LeftEdge, RightEdge); + Position.Y = height; + } + } + + public override void Render() { + if (grabbed) { + sprite.Visible = true; + sprite.Play(GrabBagModule.playerInstance.Facing == Facings.Left ? "held_l" : "held_r"); + } else { + sprite.Visible = false; + } + + base.Render(); + } + + internal static void Load() { + Everest.Events.Level.OnLoadLevel += Level_OnLoadLevel; + Everest.Events.Level.OnTransitionTo += Level_OnTransitionTo; + On.Celeste.Player.ctor += PlayerInit; + On.Celeste.Player.Update += OnPlayerUpdate; + On.Celeste.Player.UpdateSprite += UpdatePlayerVisuals; + } + + internal static void Unload() { + Everest.Events.Level.OnLoadLevel -= Level_OnLoadLevel; + Everest.Events.Level.OnTransitionTo -= Level_OnTransitionTo; + On.Celeste.Player.ctor -= PlayerInit; + On.Celeste.Player.Update -= OnPlayerUpdate; + On.Celeste.Player.UpdateSprite -= UpdatePlayerVisuals; + } + + private static void Level_OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader) { + currentGrabbed = lastGrabbed = null; + if (isFromLoader && (level.Session.StartedFromBeginning || level.Session.RestartedFromGolden) && level.Session.MapData.HasEntity("isaBag/zipline")) { + level.Session.SetFlag(NeverUsedZiplineFlag, true); + } + } + + private static void Level_OnTransitionTo(Level level, LevelData next, Vector2 direction) { + if (lastGrabbed != null) { + level.Session.SetFlag(NeverUsedZiplineFlag, false); + } + } + + private static void PlayerInit(On.Celeste.Player.orig_ctor orig, Player self, Vector2 position, PlayerSpriteMode spriteMode) { + orig(self, position, spriteMode); + ZipLineState = self.StateMachine.AddState(ZipLineUpdate, ZipLineCoroutine, ZipLineBegin, ZipLineEnd); + } + + private static void OnPlayerUpdate(On.Celeste.Player.orig_Update orig, Player self) { + orig(self); + + ziplineBuffer = Calc.Approach(ziplineBuffer, 0, Engine.DeltaTime); + if (!Input.GrabCheck) { + ziplineBuffer = 0; + } + } + + private static void UpdatePlayerVisuals(On.Celeste.Player.orig_UpdateSprite orig, Player self) { + if (ZipLineState > Player.StIntroThinkForABit && self.StateMachine == ZipLineState) { + self.Sprite.Scale.X = Calc.Approach(self.Sprite.Scale.X, 1f, 1.75f * Engine.DeltaTime); + self.Sprite.Scale.Y = Calc.Approach(self.Sprite.Scale.Y, 1f, 1.75f * Engine.DeltaTime); + + if (GrabbingCoroutine) { + return; + } + + self.Sprite.PlayOffset("fallSlow_carry", .5f, false); + self.Sprite.Rate = 0.0f; + } else { + orig(self); + } + } + + private static void ZipLineBegin() { + Player self = GrabBagModule.playerInstance; + self.Ducking = false; + self.Speed.Y = 0; + } + + private static void ZipLineEnd() { + currentGrabbed.grabbed = false; + currentGrabbed = null; + ziplineBuffer = 0.35f; + } + + private static int ZipLineUpdate() { + Player self = GrabBagModule.playerInstance; + + if (currentGrabbed == null) { + return Player.StNormal; + } + + if (!currentGrabbed.grabbed) { + return ZipLineState; + } + + currentGrabbed.speed = self.Speed.X; + + if (Math.Sign(self.LiftSpeed.X) * Math.Sign(self.Speed.X) == -1 || Math.Abs(self.LiftSpeed.X) <= Math.Abs(self.Speed.X)) { + self.LiftSpeed = self.Speed; + self.LiftSpeedGraceTime = 0.15f; + } + + int moveX = DynamicData.For(self).Get("moveX"); + if (Math.Sign(moveX) == -Math.Sign(self.Speed.X)) { + self.Speed.X = Calc.Approach(self.Speed.X, moveX * ZIP_SPEED, ZIP_TURN * Engine.DeltaTime); + } else if (Math.Abs(self.Speed.X) <= ZIP_SPEED || Math.Sign(moveX) != Math.Sign(self.Speed.X)) { + self.Speed.X = Calc.Approach(self.Speed.X, moveX * ZIP_SPEED, ZIP_ACCEL * Engine.DeltaTime); + } + + if (!Input.GrabCheck || self.Stamina <= 0) { + return Player.StNormal; + } + + if (Input.Jump.Pressed) { + Input.Jump.ConsumePress(); + + if (currentGrabbed.usesStamina) { + self.Stamina -= 110f / 8f; + } + + self.Speed.X *= 0.1f; + self.Jump(false, true); + self.LiftSpeed *= 0.4f; + + currentGrabbed.speed = Calc.Approach(currentGrabbed.speed, 0, 20); + + return Player.StNormal; + } + + if (self.CanDash) { + return self.StartDash(); + } + + if (currentGrabbed.usesStamina) { + self.Stamina -= 5 * Engine.DeltaTime; + } + + return ZipLineState; + } + + private static IEnumerator ZipLineCoroutine() { + Player self = GrabBagModule.playerInstance; + Vector2 speed = self.Speed; + self.Speed = Vector2.Zero; + currentGrabbed.speed = 0; + + self.Sprite.Play("pickup"); + + self.Play("event:/char/madeline/crystaltheo_lift", null, 0f); + + Vector2 playerLerp = new((self.X + currentGrabbed.X) / 2f, currentGrabbed.Y + 22); + + playerLerp.X = MathHelper.Clamp(playerLerp.X, currentGrabbed.LeftEdge, currentGrabbed.RightEdge); + Vector2 zipLerp = new(playerLerp.X, currentGrabbed.Y); + + Vector2 playerInit = self.Position, + zipInit = currentGrabbed.Position; + + Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.Linear, 0.07f, true); + + while (tween.Active) { + tween.Update(); + + MoveEntityTo(self, Vector2.Lerp(playerInit, playerLerp, tween.Percent)); + currentGrabbed.Position = Vector2.Lerp(zipInit, zipLerp, tween.Percent); + + yield return null; + } + + currentGrabbed.grabbed = true; + + self.Speed = speed; + + MoveEntityTo(self, playerLerp); + currentGrabbed.Position = zipLerp; + + yield break; + } + + private static void MoveEntityTo(Actor ent, Vector2 position) { + ent.MoveToX(position.X); + ent.MoveToY(position.Y); + } + + private static bool CanGrabZip(ZipLine line) { + return lastGrabbed != line || ziplineBuffer <= 0; + } + } + + public class ZipLineRender : Entity { + private static readonly Color darkLine = Calc.HexToColor("9292a9"); + private static readonly Color lightLine = Calc.HexToColor("bbc0ce"); + + private readonly List renderList = new(); + private readonly ZipLine zipInst; + private readonly Sprite sprite; + + public ZipLineRender(ZipLine instance) { + zipInst = instance; + + sprite = GrabBagModule.sprites.Create("zipline"); + sprite.Play("idle"); + sprite.JustifyOrigin(new Vector2(0.5f, 0.25f)); + Add(sprite); + + Depth = 500; + } + + public override void Render() { + renderList.Clear(); + + Position = zipInst.Position; + + Rectangle tempRect = new((int)zipInst.LeftEdge, (int)zipInst.Y, (int)(zipInst.RightEdge - zipInst.LeftEdge), 1); + tempRect.Inflate(8, 0); + + renderList.Add(new RenderRectangle(tempRect, darkLine)); + + tempRect.Y -= 1; + + renderList.Add(new RenderRectangle(tempRect, lightLine)); + + int left = tempRect.Left, right = tempRect.Right; + + renderList.Add(new RenderRectangle(new Rectangle(left - 2, (int)Y - 3, 2, 6), darkLine)); + renderList.Add(new RenderRectangle(new Rectangle(right, (int)Y - 3, 2, 6), darkLine)); + + foreach (RenderRectangle rl in renderList) { + Rectangle r = rl.rect; + r.Inflate(1, 0); + Draw.Rect(r, Color.Black); + r.Inflate(-1, 1); + Draw.Rect(r, Color.Black); + } + + foreach (RenderRectangle rl in renderList) { + Draw.Rect(rl.rect, rl.color); + } + + base.Render(); + + } + } + + public struct RenderRectangle { + public Rectangle rect; + public Color color; + + public RenderRectangle(Rectangle r, Color c) { + rect = r; + color = c; + } + } +} diff --git a/Components/CorrectDemoDash.cs b/Components/CorrectDemoDash.cs deleted file mode 100644 index b93089a..0000000 --- a/Components/CorrectDemoDash.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using Celeste.Mod; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using Monocle; -using Celeste; -using System.Reflection; -using Microsoft.Xna.Framework; - -namespace Celeste.Mod.IsaGrabBag.Components { - public class CorrectDemoDash : Component { - public CorrectDemoDash() : base(true, false) { - - onGround = typeof(Player).GetField("onGround", BindingFlags.NonPublic | BindingFlags.Instance); - } - - private const float WAIT_TIMER = -1; - - private static int newTrack; - - private Player maddy; - - private FieldInfo onGround; - IEnumerator dashinitial; - bool inDemoSpot, wasDemoSpot; - - public override void Added(Entity entity) { - maddy = entity as Player; - base.Added(entity); - - var coroutines = typeof(StateMachine).GetField("coroutines", BindingFlags.Instance | BindingFlags.NonPublic); - - Func[] list = coroutines.GetValue(maddy.StateMachine) as Func[]; - - dashinitial = list[2].Invoke(); - list[2] = new Func(ContinueDashing); - } - - private IEnumerator ContinueDashing() { - - inDemoSpot = false; - wasDemoSpot = false; - - dashinitial = typeof(Player).GetMethod("DashCoroutine", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(maddy, null) as IEnumerator; - - while (dashinitial.MoveNext()) { - if (dashinitial.Current is float) { - - float value = (float)dashinitial.Current; - - yield return value; - - - if (wasDemoSpot && maddy.Speed.X != 0) { - while (inDemoSpot) { - if (Input.DashPressed) - break; - yield return null; - } - - if (!Input.DashPressed) - yield return null; - } - } - else - yield return dashinitial.Current; - } - - yield break; - } - - - public override void Update() { - - base.Update(); - - inDemoSpot = false; - - const int testHeight = 4; - - var top = maddy.Collider.Bounds; - try { - if (maddy.StateMachine == 2 && top.Height <= 6) { - int height = top.Height; - - top.Height = testHeight; - top.Y = (int)maddy.Top - top.Height;// testHeight; - - var bottom = top; - bottom.Y += height + testHeight; - - var spikeTest = top; - spikeTest.Y -= 8; - spikeTest.Height += 8; - - if ((Scene.CollideCheck(top) || Scene.CollideCheck(spikeTest)) && - (Scene.CollideCheck(bottom))) { - inDemoSpot = true; - wasDemoSpot = true; - - maddy.StartJumpGraceTime(); - maddy.RefillDash(); - maddy.RefillStamina(); - onGround.SetValue(maddy, true); - maddy.Speed.Y = 0f; - maddy.Position.Y = Calc.Snap(maddy.Position.Y, 8); - } - - } - } - catch { } - - } - } -} diff --git a/Components/WaterBoostMechanic.cs b/Components/WaterBoostMechanic.cs deleted file mode 100644 index 6a5283c..0000000 --- a/Components/WaterBoostMechanic.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Monocle; -using Microsoft.Xna.Framework; - -namespace Celeste.Mod.IsaGrabBag -{ - public class WaterBoostMechanic : Entity - { - public WaterBoostMechanic(EntityData _data) - { - GrabBagModule.GrabBagMeta.WaterBoost = _data.Bool("boostEnabled"); - } - } -} diff --git a/CornerBoostBlock.cs b/CornerBoostBlock.cs deleted file mode 100644 index 06bbc66..0000000 --- a/CornerBoostBlock.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using Microsoft.Xna.Framework; -using Monocle; -using Celeste; -using Celeste.Mod; -using System.Reflection; - -namespace Celeste.Mod.IsaGrabBag { - [Entities.CustomEntity("isaBag/cornerBlock")] - [Tracked(false)] - public class CornerBoostBlock : Solid { - - - - private static FieldInfo retentionTimer, retentionSpeed, moveX; - - static CornerBoostBlock() { - retentionTimer = typeof(Player).GetField("wallSpeedRetentionTimer", BindingFlags.NonPublic | BindingFlags.Instance); - retentionSpeed = typeof(Player).GetField("wallSpeedRetained", BindingFlags.NonPublic | BindingFlags.Instance); - moveX = typeof(Player).GetField("moveX", BindingFlags.NonPublic | BindingFlags.Instance); - } - - public bool CustomTile => _tileset != '\0'; - - char _tileset; - TileGrid tiles; - List Group = new List(); - - bool HasGroup; - - Point GroupBoundsMin, GroupBoundsMax; - - public CornerBoostBlock(Vector2 position, int width, int height, char tile, bool useTileset) : base(position, width, height, true) { - if (useTileset) { - _tileset = tile; - } - else { - _tileset = '\0'; - } - - OnCollide = OnCollision; - } - public CornerBoostBlock(EntityData data, Vector2 offset) : this(data.Position + offset, data.Width, data.Height, data.Char("tiletype", '3'), data.Bool("useTileset", false)) { } - - private void OnCollision(Vector2 dir) { - - var player = GrabBagModule.playerInstance; - if (player == null) - player = Scene.Entities.FindFirst(); - - if (dir.X == 0 || player == null) - return; - - if ((float)retentionTimer.GetValue(player) == 0.06f) { - retentionTimer.SetValue(player, 0.12f); - } - } - - public override void Awake(Scene scene) { - - if (!HasGroup) { - - GroupBoundsMin = new Point((int)X, (int)Y); - GroupBoundsMax = new Point((int)Right, (int)Bottom); - - AddToGroupAndFindChildren(this); - - Rectangle rectangle = new Rectangle(GroupBoundsMin.X / 8, GroupBoundsMin.Y / 8, (GroupBoundsMax.X - GroupBoundsMin.X) / 8, (GroupBoundsMax.Y - GroupBoundsMin.Y) / 8); - VirtualMap virtualMap = new VirtualMap(rectangle.Width, rectangle.Height, '0'); - - - foreach (CornerBoostBlock block in Group) { - int left = (int)(block.X / 8f) - rectangle.X; - int top = (int)(block.Y / 8f) - rectangle.Y; - int right = (int)(block.Width / 8f); - int bottom = (int)(block.Height / 8f); - - for (int x = left; x < left + right; x++) { - for (int y = top; y < top + bottom; y++) { - - virtualMap[x, y] = CustomTile ? _tileset : '3'; - } - } - } - - tiles = GFX.FGAutotiler.GenerateMap(virtualMap, new Autotiler.Behaviour { - EdgesExtend = false, - EdgesIgnoreOutOfLevel = false, - PaddingIgnoreOutOfLevel = false - }).TileGrid; - - if (!CustomTile) { - var template = tiles; - - tiles = new TileGrid(8, 8, rectangle.Width, rectangle.Height); - - var tex = GFX.Game["isafriend/tilesets/boost_block"]; - - Vector2 texOffset = new Vector2(0.255127f, 0.4956055f); - Vector2 texSize = new Vector2(0.01164f, 0.0291f); - - Tileset tileset = new Tileset(tex, 8, 8); - - - const int width = 6, height = 15; - - for (int y = 0; y < rectangle.Height; ++y) { - for (int x = 0; x < rectangle.Width; ++x) { - - var fromTemplate = template.Tiles[x, y]; - - if (fromTemplate == null) - continue; - - float u = (fromTemplate.LeftUV - texOffset.X) / texSize.X; - float v = (fromTemplate.TopUV - texOffset.Y) / texSize.Y; - - if (u < 0 && u > -0.0001f) - u = 0; - if (v < 0 && v > -0.0001f) - v = 0; - - if (u < 0 || v < 0 || u >= 1 || v >= 1) - break; - - tiles.Tiles[x, y] = tileset[(int)(u * width), (int)(v * height)]; - } - } - } - - tiles.Position = new Vector2(GroupBoundsMin.X - X, GroupBoundsMin.Y - Y); - - Add(tiles); - } - - base.Awake(scene); - } - - private void AddToGroupAndFindChildren(CornerBoostBlock from) { - - if (from.X < GroupBoundsMin.X) { - GroupBoundsMin.X = (int)from.X; - } - if (from.Y < GroupBoundsMin.Y) { - GroupBoundsMin.Y = (int)from.Y; - } - if (from.Right > GroupBoundsMax.X) { - GroupBoundsMax.X = (int)from.Right; - } - if (from.Bottom > GroupBoundsMax.Y) { - GroupBoundsMax.Y = (int)from.Bottom; - } - from.HasGroup = true; - Group.Add(from); - if (from != this) { - //from.master = this; - } - - foreach (Entity entity in Scene.Tracker.GetEntities()) { - CornerBoostBlock block = (CornerBoostBlock)entity; - - if (!block.HasGroup && block._tileset == _tileset && (Scene.CollideCheck(new Rectangle((int)from.X - 1, (int)from.Y, (int)from.Width + 2, (int)from.Height), block) || base.Scene.CollideCheck(new Rectangle((int)from.X, (int)from.Y - 1, (int)from.Width, (int)from.Height + 2), block))) { - AddToGroupAndFindChildren(block); - } - } - } - - public static void Load() { - On.Celeste.Player.ClimbJump += OnClimbJumped; - On.Celeste.Player.ClimbEnd += Player_ClimbEnd; - On.Celeste.Player.NormalEnd += Player_NormalEnd; - } - - - public static void Unload() { - On.Celeste.Player.ClimbJump -= OnClimbJumped; - On.Celeste.Player.ClimbEnd -= Player_ClimbEnd; - On.Celeste.Player.NormalEnd -= Player_NormalEnd; - } - - private static Rectangle GetFacingHitbox(Player player) { - - Rectangle hitbox = player.Collider.Bounds; - - hitbox.Width = 1; - - if (player.Facing == Facings.Left) { - hitbox.X -= 1; - } - else { - hitbox.X += (int)player.Width; - } - hitbox.Y -= 1; - hitbox.Height += 1; - - return hitbox; - } - - private static void Player_ClimbEnd(On.Celeste.Player.orig_ClimbEnd orig, Player self) { - float timer = (float)retentionTimer.GetValue(self); - - orig(self); - - if (self.Scene.CollideCheck(GetFacingHitbox(self))) - retentionTimer.SetValue(self, timer); - } - - private static void Player_NormalEnd(On.Celeste.Player.orig_NormalEnd orig, Player self) { - float timer = (float)retentionTimer.GetValue(self); - - orig(self); - - if (self.StateMachine.State == 1 && self.Scene.CollideCheck(GetFacingHitbox(self))) { - - retentionTimer.SetValue(self, timer); - - } - } - - private static void OnClimbJumped(On.Celeste.Player.orig_ClimbJump orig, Player self) { - orig(self); - - if (self.Scene.CollideCheck(GetFacingHitbox(self)) && (float)retentionTimer.GetValue(self) > 0 && (float)retentionSpeed.GetValue(self) != 0) { - - retentionTimer.SetValue(self, Math.Max((float)retentionTimer.GetValue(self), 0.06f)); - - float speed = (float)retentionSpeed.GetValue(self); - retentionSpeed.SetValue(self, speed + (40 * (int)moveX.GetValue(self))); - - } - } - } -} \ No newline at end of file diff --git a/CustomNPC.cs b/CustomNPC.cs deleted file mode 100644 index a493785..0000000 --- a/CustomNPC.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.Xna.Framework; -using System.Collections; -using Monocle; - -namespace Celeste.Mod.IsaGrabBag -{ - class CustomNPC : NPC - { - private string npcValue, dialogue; - private bool facingLeft; - private BadelineDummy baddyDummy; - - public CustomNPC(Vector2 position, EntityData data) : base(position) - { - facingLeft = data.Bool("faceLeft", false); - npcValue = data.Attr("npc", "granny"); - - switch (npcValue) - { - default: - Add(Sprite = GFX.SpriteBank.Create(npcValue)); - break; - case "oshiro": - Add(Sprite = new OshiroSprite(facingLeft ? -1 : 1)); - break; - case "badeline": - Add(Sprite = (baddyDummy = new BadelineDummy(position)).Sprite); - break; - } - } - } - - delegate IEnumerator NPCAction(string value); - - class CS_CustomNPC : CutsceneEntity - { - private const int ACTIONS_EXTRA = 1; - public NPCAction[] actions; - - private string dialogue; - - public CS_CustomNPC(string dialogue, params NPCAction[] _actions) - { - actions = new NPCAction[_actions.Length + ACTIONS_EXTRA]; - for (int i = _actions.Length; i < _actions.Length + ACTIONS_EXTRA; i++) - { - - } - } - - public override void OnBegin(Level level) - { - Add(new Coroutine(Cutscene())); - } - - public IEnumerator Cutscene() - { - //yield return Textbox.Say(dialogue, actions); - yield break; - } - public override void OnEnd(Level level) - { - - } - } -} diff --git a/CustomPlayer.cs b/CustomPlayer.cs deleted file mode 100644 index 6ed8fea..0000000 --- a/CustomPlayer.cs +++ /dev/null @@ -1,257 +0,0 @@ -// using System; -// using System.Collections.Generic; -// using System.Linq; -// using System.Text; -// using System.Threading.Tasks; -// using System.IO; -// using Microsoft.Xna.Framework; -// using Monocle; -// using NLua; - -// namespace Celeste.Mod.IsaGrabBag -// { -// static class LuaEXT { -// public static bool ContainsKey(this LuaTable _table, object _key) { -// foreach (var obj in _table.Keys) -// if (obj == _key) -// return true; - -// return false; -// } -// public static bool ContainsValue(this LuaTable _table, object _value) { -// foreach (var obj in _table.Values) -// if (obj == _value) -// return true; - -// return false; -// } -// public static int Count(this LuaTable _table) { -// int count = 0; -// foreach (var obj in _table.Keys) -// count++; - -// return count; -// } -// public static int Integer(this LuaTable _table, object _key, int _default = 0) { -// var obj = _table[_key]; -// if (obj == null) -// return _default; - -// switch (obj) { -// case long l: -// return (int)l; -// case double d: -// return (int)d; -// default: -// return _default; -// } -// } -// public static float Float(this LuaTable _table, object _key, float _default = 0) { -// var obj = _table[_key]; -// if (obj == null) -// return _default; - -// switch (obj) { -// case long l: -// return (float)l; -// case double d: -// return (float)d; -// default: -// return _default; -// } -// } -// } -// public class CustomPlayer : Actor { -// public static void LeaveLevel(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) { -// instance = null; -// } - -// public static CustomPlayer instance { get; private set; } - -// public static CustomPlayer GetPlayer() { -// return instance; -// } - -// public Vector2 Speed; -// public float CoyoteTime; - -// public string PlayerType { get; private set; } -// LuaTable functions; -// StateMachine machine; - -// private Entity playerFollower; - -// private string[] stateNames; - -// private int updateErrorWait; - -// public CustomPlayer(EntityData _data, Vector2 offset) : base(_data.Position + offset) { - -// PlayerType = _data.Attr("entity", "IsaExample"); -// string path = Path.Combine("LuaPlayer", PlayerType, "main"); - -// var cont = Everest.Content.Get(path); - -// if (cont == null) -// return; - -// try { -// functions = Everest.LuaLoader.Context.DoString(cont.Data)[0] as LuaTable; - -// bool start = instance == null; - -// if (instance == null) -// instance = this; - -// instance.InitCharacter(functions, start); - -// Tag = Tags.Persistent; -// } -// catch (Exception e) { -// Logger.Log("Lua Player", $"Error Loading player module : {e}"); -// instance = null; -// return; -// } - -// } -// private void InitCharacter(LuaTable code, bool isInit) { - -// var initFunc = code["init"] as LuaFunction; - -// if (initFunc != null) -// initFunc.Call(isInit); - -// Collider = new Hitbox(8, 8); -// functions = code; - -// if (!isInit) { -// Remove(machine); -// } - -// foreach (var obj in code.Keys) { -// Logger.Log("Lua Character", $"\"{obj}\" : \"{code[obj].GetType()}\""); -// } - -// var array = code["states"] as LuaTable; - -// Add(machine = new StateMachine(array.Keys.Count)); -// stateNames = new string[array.Keys.Count]; -// int index = 0; - -// foreach (var key in array.Keys) { - -// stateNames[index] = array[key] as string; -// machine.ReflectState(this, index, "machine"); -// index++; -// } - -// } - -// public override void Awake(Scene scene) { - -// if (instance != this) { -// if (instance == null) { - -// GrabBagModule.playerInstance.Active = true; -// GrabBagModule.playerInstance.Visible = true; -// GrabBagModule.playerInstance.Collidable = true; -// } -// RemoveSelf(); -// return; -// } - -// GrabBagModule.playerInstance.Active = false; -// GrabBagModule.playerInstance.Visible = false; -// GrabBagModule.playerInstance.Collidable = false; -// playerFollower = GrabBagModule.playerInstance; -// BottomCenter = playerFollower.BottomCenter; - -// base.Awake(scene); -// } - -// public override void Update() { -// base.Update(); - -// CoyoteTime -= Engine.DeltaTime; - -// if (Input.Jump.Pressed && CoyoteTime > 0) { -// Input.Jump.ConsumeBuffer(); -// CoyoteTime = 0; -// Speed.Y = -4; -// } - -// //Speed.X = Calc.Approach(Speed.X, Input.MoveX * 3, 8 * Engine.DeltaTime); -// Speed.Y = Calc.Approach(Speed.Y, functions.Float("max_fall", 4), functions.Float("grav") * Engine.DeltaTime); - -// MoveH(Speed.X * Engine.DeltaTime * 60, CollideH); -// MoveV(Speed.Y * Engine.DeltaTime * 60, CollideV); - -// playerFollower.Center = Center; -// } - -// private int machineUpdate() { - -// var function = functions[$"{stateNames[machine]}_update"] as LuaFunction; - -// try { - - -// if (function != null) { - -// int newState = -1; -// object[] array = function.Call(); - -// if (array != null && array.Length > 0) { - -// if (array[0] is string) { -// for (int i = 0; i < stateNames.Length; ++i) { -// if (stateNames[i] == array[0] as string) { -// newState = i; -// } -// } -// } -// else -// newState = (int)array[0]; -// } - -// if (newState != -1) -// return newState; -// } - -// if (updateErrorWait > 0) -// updateErrorWait++; -// } -// catch (Exception e) { -// if (updateErrorWait == 0) -// Logger.Log("Lua Character", $"Exception caught in custom player update \"{machine.State}\": {e}"); - -// updateErrorWait++; -// } - -// if (updateErrorWait == 60) -// updateErrorWait = 0; - -// return machine; -// } - -// private void CollideH(CollisionData hitData) { -// Speed.X = 0; -// } -// private void CollideV(CollisionData hitData) { -// Speed.Y = 0; -// CoyoteTime = 0.1f; -// } - -// public static void Walk(float max_speed, float accel) { - -// Logger.Log("Lua Char", "Hello"); -// instance.Speed.X = Calc.Approach(instance.Speed.X, Input.MoveX * max_speed, accel * Engine.DeltaTime); -// } - -// public override void Render() { -// base.Render(); - -// Draw.Rect(Collider, Color.White); -// } -// } -// } diff --git a/DashSpinners.cs b/DashSpinners.cs deleted file mode 100644 index 6c1aee4..0000000 --- a/DashSpinners.cs +++ /dev/null @@ -1,516 +0,0 @@ -using Celeste; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Monocle; -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Celeste.Mod.IsaGrabBag -{ - public class DreamSpinner : Entity - { - private static bool dreamdashEnabled = true; - private DreamBlock block; - private bool hasCollided, fake; - - public List spinners; - public List fillOffset; - - public int RNG; - - public bool Fragile; - - private static readonly Color debrisColor = new Color(0xC1, 0x8A, 0x53); - - public DreamSpinner(EntityData data, Vector2 offset, bool _fake) - : this(data.Position + offset, data.Bool("useOnce", false), _fake) - { - } - - public DreamSpinner(Vector2 position, bool _useOnce, bool _fake) - { - Position = position; - - spinners = new List(); - fillOffset = new List(); - - Collider = new ColliderList( - new Hitbox(16, 4, -8, -3), - new Circle(6) - ); - - Add(new PlayerCollider(OnPlayer)); - Add(new LedgeBlocker()); - - Fragile = _useOnce; - fake = _fake; - Visible = false; - RNG = Calc.Random.Next(4); - Depth = -8500; - Collidable = false; - } - - private void OnPlayer(Player player) { - player.Die((player.Center - Center).SafeNormalize()); - } - - public override void Update() - { - if (fake) - return; - - Player player = GrabBagModule.playerInstance; - - foreach (var deb in Scene.CollideAll(block.Collider.Bounds)) - { - if (deb is CrystalDebris) - deb.RemoveSelf(); - } - - if (player != null) - { - dreamdashEnabled = player.Inventory.DreamDash; - if (Fragile) - { - bool isColliding = block.Collidable && player.Collider.Collide(block); - - if (!isColliding && hasCollided) - { - RemoveSelf(); - block.RemoveSelf(); - - Audio.Play("event:/game/06_reflection/fall_spike_smash", Position); - CrystalDebris.Burst(Center, debrisColor, false, 4); //c18a53 - return; - } - hasCollided = isColliding; - } - if ((player.DashAttacking || player.StateMachine.State == 9) && dreamdashEnabled) - { - block.Collidable = true; - Collidable = false; - } - else - { - block.Collidable = false; - Collidable = true; - - } - } - } - - public override void Removed(Scene scene) - { - base.Removed(scene); - } - - public override void Added(Scene scene) - { - base.Added(scene); - - if (!Fragile) - { - foreach (var spin in SceneAs().Entities.FindAll()) - { - if (spin == this || spin.Fragile || spin.spinners.Contains(this)) - continue; - - if (Vector2.Distance(spin.Position, Position) < 24) - { - spinners.Add(spin); - fillOffset.Add((spin.Center + Center) / 2); - } - } - } - - if (!fake) - { - scene.Add(block = new DreamBlock(Center - new Vector2(8, 8), 16, 16, null, false, false)); - block.Visible = false; - - if (GrabBagModule.GrabBagMeta.RoundDreamSpinner) - block.Collider = new Circle(9f, 8, 8); - } - } - - public bool InView() - { - Camera camera = (Scene as Level).Camera; - return X > camera.X - 16f && Y > camera.Y - 16f && X < camera.X + 320f + 16f && Y < camera.Y + 180f + 16f; - } - } - - struct Star - { - public Star(Vector2 _pos, int _depth, float _animSpeed, float _initAnim, byte _color) - { - point = _pos; - depth = _depth; - animSpeed = _animSpeed; - anim = _initAnim; - color = _color; - } - public Vector2 point; - public int depth; - public byte color; - public float anim, animSpeed; - } - class DreamSpinnerBorder : Entity - { - internal static DreamSpinnerBorder mainInstance; - - public static void ReloadBorder(Scene scene) { - - if (mainInstance != null && scene != mainInstance.Scene) { - mainInstance.RemoveSelf(); - mainInstance = null; - } - if (mainInstance == null) { - scene.Add(new DreamSpinnerBorder()); - } - } - - static Image spinnerMainTex, spinnerBGTex; - static Image[] starTextures; - static Effect borderEffect, starEffect, spinnerEffect; - static DepthStencilState spinnerState; - static DepthStencilState starState; - static RenderTarget2D mainTarget; - - - static bool dreamDashEnabled; - - static GameplayRenderer gameplayInstance; - - static Star[] stars; - - static Color[][] colors = new Color[][]{ - new Color[]{ - Calc.HexToColor("6383b8"), - Calc.HexToColor("ba4a4a"), - Calc.HexToColor("f0ff00"), - Calc.HexToColor("9800b2"), - Calc.HexToColor("9800b2"), - Calc.HexToColor("3ca31d"), - Calc.HexToColor("72e0ea") - }, - new Color[]{ - Color.Orange, - Color.Orange, - Color.Orange, - Color.Orange, - Color.Orange, - Color.Orange, - Color.Orange, - }, - new Color[]{ - Calc.HexToColor("666666"), - Calc.HexToColor("666666"), - Calc.HexToColor("666666"), - Calc.HexToColor("666666"), - Calc.HexToColor("666666"), - Calc.HexToColor("666666"), - Calc.HexToColor("666666") - } - - }; - - public const int SPIN_TYPE_COUNT = 4; - public const int SPRITE_MAIN = 21, SPRITE_FILLER = 14; - - const int starCount = 700; - const int depthDiv = starCount / 5; - - private const int WIDTH = 320 + 2, HEIGHT = 180 + 2; - - private Vector2 camPos, lastCamPos; - - public static void LoadTextures() { - - spinnerState = new DepthStencilState(); - starState = new DepthStencilState(); - - mainTarget = new RenderTarget2D(Draw.SpriteBatch.GraphicsDevice, WIDTH, HEIGHT, false, SurfaceFormat.Color, DepthFormat.Depth16); - - spinnerState.DepthBufferFunction = CompareFunction.Greater; - spinnerState.DepthBufferEnable = true; - spinnerState.DepthBufferWriteEnable = true; - spinnerState.StencilDepthBufferFail = StencilOperation.Keep; - spinnerState.StencilPass = StencilOperation.Replace; - - starState.DepthBufferFunction = CompareFunction.Equal; - starState.DepthBufferEnable = true; - starState.DepthBufferWriteEnable = false; - starState.StencilDepthBufferFail = StencilOperation.Keep; - starState.StencilPass = StencilOperation.Keep; - - spinnerMainTex = new Image(GFX.Game["isafriend/danger/crystal/fg_dream"]); - spinnerBGTex = new Image(GFX.Game["isafriend/danger/crystal/bg_dream"]); - - starTextures = new Image[] { - new Image(GFX.Game["isafriend/danger/crystal/star0"]), - new Image(GFX.Game["isafriend/danger/crystal/star1"]), - new Image(GFX.Game["isafriend/danger/crystal/star2"]), - null - }; - starTextures[3] = starTextures[1]; - - spinnerMainTex.CenterOrigin(); - spinnerBGTex.CenterOrigin(); - - try { - var asset = Everest.Content.Get("Effects/dream_border.cso"); - borderEffect = new Effect(Draw.SpriteBatch.GraphicsDevice, asset.Data); - - asset = Everest.Content.Get("Effects/dream_stars.cso"); - starEffect = new Effect(Draw.SpriteBatch.GraphicsDevice, asset.Data); - - asset = Everest.Content.Get("Effects/dream_spinners.cso"); - spinnerEffect = new Effect(Draw.SpriteBatch.GraphicsDevice, asset.Data); - } - catch (Exception e) { - Logger.Log(LogLevel.Error, "IsaGrabBag", "Failed to load the shader"); - Logger.Log(LogLevel.Error, "IsaGrabBag", "Exception: \n" + e.ToString()); - } - - stars = new Star[starCount]; - - Calc.PushRandom(0x12F3); - - for (int i = 0; i < starCount; ++i) { - int depth = i / depthDiv; - depth = 4 - depth; - - float initAnim = 0; - if (depth < 2) { - initAnim = Calc.Random.NextFloat(4f); - } - else if (depth < 4) { - initAnim = Calc.Random.NextFloat(2f); - } - stars[i] = new Star( - new Vector2(Calc.Random.NextFloat(WIDTH), Calc.Random.NextFloat(HEIGHT)), - depth, Calc.Random.NextFloat(1f) + 5f, initAnim, (byte)Calc.Random.Next(0, 7)); - } - - Calc.PopRandom(); - } - public static void OnLoad() { - On.Celeste.GameplayRenderer.ctor += GameplayRenderer_ctor; - Everest.Events.Level.OnExit += Level_OnExit; - On.Celeste.Level.ctor += Level_ctor; - - } - public static void OnUnload() { - On.Celeste.GameplayRenderer.ctor -= GameplayRenderer_ctor; - Everest.Events.Level.OnExit -= Level_OnExit; - On.Celeste.Level.ctor -= Level_ctor; - - } - - private static void Level_ctor(On.Celeste.Level.orig_ctor orig, Level self) { - orig(self); - ReloadBorder(self); - } - - private static void Level_OnExit(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) { - if (mainInstance != null) { - - mainInstance.RemoveSelf(); - mainInstance = null; - } - } - private static void GameplayRenderer_ctor(On.Celeste.GameplayRenderer.orig_ctor orig, GameplayRenderer self) { - orig(self); - gameplayInstance = self; - } - - - - public DreamSpinnerBorder() { - Depth = -8500; - - mainInstance = this; - AddTag(Tags.Global); - } - - public override void Awake(Scene scene) - { - Update(); - base.Awake(scene); - } - public override void Added(Scene scene) - { - base.Added(scene); - - camPos = SceneAs().Camera.Position; - camPos.X = (int)camPos.X; - camPos.Y = (int)camPos.Y; - lastCamPos = camPos; - } - - public override void Update() - { - base.Update(); - - if (GrabBagModule.playerInstance != null && !GrabBagModule.playerInstance.Dead) { - dreamDashEnabled = GrabBagModule.playerInstance.Inventory.DreamDash; - } - - if (dreamDashEnabled && mainInstance == this) { - - for (int i = 0; i < starCount; ++i) { - - if (stars[i].depth < 2) { - stars[i].anim += stars[i].animSpeed * Engine.DeltaTime; - if (stars[i].anim >= 4) - stars[i].anim -= 4; - } - else if (stars[i].depth < 4) { - stars[i].anim += stars[i].animSpeed * Engine.DeltaTime; - if (stars[i].anim >= 2) - stars[i].anim -= 2; - } - } - - } - - base.Update(); - } - - public override void Render() - { - base.Render(); - - if (Scene.Entities.FindFirst() == null) { - return; - } - - camPos = SceneAs().Camera.Position; - camPos.X = (int)camPos.X; - camPos.Y = (int)camPos.Y; - - for (int i = 0; i < starCount; ++i) { - - Vector2 point = stars[i].point; - point -= (camPos - lastCamPos) * (5 - stars[i].depth) * 0.15f; - if (point.X < -4) - point.X += WIDTH + 4; - if (point.X >= WIDTH) - point.X -= WIDTH + 4; - if (point.Y < -4) - point.Y += HEIGHT + 4; - if (point.Y >= HEIGHT) - point.Y -= HEIGHT + 4; - - stars[i].point = point; - - } - - lastCamPos = camPos; - - - - GameplayRenderer.End(); - DrawTexture(false); - DrawTexture(true); - GameplayRenderer.Begin(); - } - - private void DrawTexture(bool isFragile) { - - if (GrabBagModule.playerInstance != null && !GrabBagModule.playerInstance.Dead) { - dreamDashEnabled = GrabBagModule.playerInstance.Inventory.DreamDash; - } - - var gd = Draw.SpriteBatch.GraphicsDevice; - - gd.SetRenderTarget(mainTarget); - gd.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Transparent, 0f, 0); - - // Start rendering dream spinners - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, spinnerState, RasterizerState.CullNone, - spinnerEffect, gameplayInstance.Camera.Matrix); - - if (!dreamDashEnabled) { - spinnerMainTex.Color = new Color(25, 25, 25); - } - else if (isFragile) { - spinnerMainTex.Color = new Color(30, 22, 10); - } - else { - spinnerMainTex.Color = Color.Black; - } - spinnerBGTex.Color = spinnerMainTex.Color; - - foreach (var spin in Scene.Entities.FindAll()) { - if (spin.Fragile != isFragile || !spin.InView()) - continue; - - int type = spin.RNG; - - spinnerMainTex.RenderPosition = spin.Position; - spinnerMainTex.Rotation = (type & 0x3) * MathHelper.PiOver2; - - spinnerMainTex.Render(); - - foreach (Vector2 pos in spin.fillOffset) { - spinnerBGTex.RenderPosition = pos; - spinnerBGTex.Render(); - } - } - - Draw.SpriteBatch.End(); - - - // Draw stars on spinner - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, starState, RasterizerState.CullNone, - starEffect, Matrix.Identity); - - Color[] array; - - if (!dreamDashEnabled) { - array = colors[2]; - } - else if (isFragile) { - array = colors[1]; - } - else { - array = colors[0]; - } - - - for (int i = 0; i < starCount; ++i) { - - var image = starTextures[(int)stars[i].anim]; - if (isFragile && stars[i].depth < 2) - image = starTextures[0]; - image.Color = array[stars[i].color]; - image.RenderPosition = stars[i].point; - image.Render(); - } - - Draw.SpriteBatch.End(); - - - gd.SetRenderTarget(GameplayBuffers.Gameplay); - - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, - borderEffect, gameplayInstance.Camera.Matrix); - - if (!dreamDashEnabled) { - Draw.SpriteBatch.Draw(mainTarget, gameplayInstance.Camera.Position, Color.Gray); - } - else if (isFragile) { - Draw.SpriteBatch.Draw(mainTarget, gameplayInstance.Camera.Position, Color.Orange * 0.9f); - } - else { - Draw.SpriteBatch.Draw(mainTarget, gameplayInstance.Camera.Position, Color.White); - } - - Draw.SpriteBatch.End(); - - } - } -} \ No newline at end of file diff --git a/Effects/glitchy_effect.cso b/Effects/glitchy_effect.cso new file mode 100644 index 0000000..29cecfb Binary files /dev/null and b/Effects/glitchy_effect.cso differ diff --git a/ForceVariant.cs b/ForceVariant.cs deleted file mode 100644 index 10b4c01..0000000 --- a/ForceVariant.cs +++ /dev/null @@ -1,411 +0,0 @@ -using Microsoft.Xna.Framework; -using Monocle; -using System.Reflection; -using System.Collections.Generic; -using System; -using MonoMod.Cil; -using MonoMod.RuntimeDetour; -using MonoMod.Utils; - - -namespace Celeste.Mod.IsaGrabBag { - - public enum Variant { - Hiccups = 0, - InfiniteStamina = 1, - Invincible = 2, - InvisibleMotion = 3, - LowFriction = 4, - MirrorMode = 5, - NoGrabbing = 6, - PlayAsBadeline = 7, - SuperDashing = 8, - ThreeSixtyDashing = 9, - DashAssist = 10 - } - public enum VariantState { - Enabled, - Disabled, - EnabledTemporary, - DisabledTemporary, - EnabledPermanent, - DisabledPermanent, - Toggle, - SetToDefault - } - - public static class ForceVariants { - - - static bool[] Variants_Default { get; set; } = new bool[] { false, false, false, false, false, false, false, false, false, false, false }; - static bool?[] Variants { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null, null }; - - static Dictionary itemList = new Dictionary(); - - static Hook enableHook, disableHook, aPressHook; - - static TextMenu variantMenu; - - public static void Unload() { - On.Celeste.Level.AssistMode -= Level_AssistMode; - On.Celeste.Level.VariantMode -= Level_VariantMode; - - On.Celeste.TextMenu.Close -= TextMenu_Close; - enableHook?.Dispose(); - disableHook?.Dispose(); - aPressHook?.Dispose(); - } - public static void Load() { - On.Celeste.Level.AssistMode += Level_AssistMode; - On.Celeste.Level.VariantMode += Level_VariantMode; - - disableHook = new Hook( - typeof(TextMenu.Option).GetMethod("LeftPressed", BindingFlags.Instance | BindingFlags.Public), - (Action>)OnChange); - - enableHook = new Hook( - typeof(TextMenu.Option).GetMethod("RightPressed", BindingFlags.Instance | BindingFlags.Public), - (Action>)OnChange); - - aPressHook = new Hook( - typeof(TextMenu.Option).GetMethod("ConfirmPressed", BindingFlags.Instance | BindingFlags.Public), - (Action>)OnChange); - } - - delegate void OptionChanged(TextMenu.Option self); - private static void OnChange(OptionChanged orig, TextMenu.Option self) { - orig(self); - - if (itemList.ContainsKey(self)) { - - bool value = self.Index >= 1; - - int index = itemList[self]; - - Variants_Default[index] = value; - - } - - } - - - private static void Level_VariantMode(On.Celeste.Level.orig_VariantMode orig, Level self, int returnIndex, bool minimal) { - - orig(self, returnIndex, minimal); - - List list = self.Entities.ToAdd; - OnVariantMenu(list[list.Count - 1] as TextMenu, false); - - - } - private static void Level_AssistMode(On.Celeste.Level.orig_AssistMode orig, Level self, int returnIndex, bool minimal) { - - orig(self, returnIndex, minimal); - - List list = self.Entities.ToAdd; - OnVariantMenu(list[list.Count - 1] as TextMenu, true); - } - - private readonly static List menuLayout = new List(){ - Variant.MirrorMode, - Variant.ThreeSixtyDashing, - Variant.InvisibleMotion, - Variant.NoGrabbing, - Variant.LowFriction, - Variant.SuperDashing, - Variant.Hiccups, - Variant.PlayAsBadeline, - Variant.InfiniteStamina, - Variant.DashAssist, - Variant.Invincible, - }; - - private static void OnVariantMenu(TextMenu menu, bool assist) { - - variantMenu = menu; - - var session = GrabBagModule.Session; - - itemList = new Dictionary(); - - On.Celeste.TextMenu.Close += TextMenu_Close; - - int index = assist ? 8 : 0; - for (int i = 0; i < menu.Items.Count; ++i) { - - var item = menu.Items[i]; - - if (!(item is TextMenu.OnOff)) - continue; - - Variant v = menuLayout[index++]; - - itemList.Add(item, (int)v); - - if (Variants[(int)v] != null) { - item.Disabled = true; - } - } - } - - - private static void TextMenu_Close(On.Celeste.TextMenu.orig_Close orig, TextMenu self) { - orig(self); - if (variantMenu != self) - return; - - On.Celeste.TextMenu.Close -= TextMenu_Close; - } - - public static void GetDefaults() { - - for (int i = 0; i < Variants_Default.Length; i++) { - Variants_Default[i] = GetVariantStatus((Variant)i); - } - WallToggleData.GetDefaults(); - } - public static void SaveToSession() { - - var session = GrabBagModule.Session; - - for (int i = 0; i < Variants.Length; i++) { - session.Variants_Save[i] = Variants[i]; - } - WallToggleData.SaveToSession(); - } - public static void GetFromSession() { - - var session = GrabBagModule.Session; - - for (int i = 0; i < Variants.Length; i++) { - if (Variants[i] != null && session.Variants_Save[i] == null) { - SetVariant(i, VariantState.SetToDefault); - } - Variants[i] = session.Variants_Save[i]; - } - - WallToggleData.ResetSession(); - } - public static void ReinforceSession() { - var session = GrabBagModule.Session; - - for (int i = 0; i < 11; i++) { - Variants[i] = session.Variants_Save[i]; - - if (session.Variants_Save[i] == null) - continue; - - SetVariant(i, session.Variants_Save[i].Value ? VariantState.EnabledPermanent : VariantState.DisabledPermanent); - } - - WallToggleData.ReinforceSession(); - } - public static void ResetSession() { - - for (int i = 0; i < Variants_Default.Length; i++) { - SetVariant(i, VariantState.SetToDefault); - } - - WallToggleData.ResetSession(); - } - - public static void SetVariant(int variant, VariantState state) { - - SetVariant((Variant)variant, state); - } - public static void SetVariant(Variant variant, VariantState state) { - - if (state == VariantState.SetToDefault) { - - var session = GrabBagModule.Session; - - Variants[(int)variant] = null; - SetVariantInGame(variant, Variants_Default[(int)variant]); - - return; - } - - bool permanent = state == VariantState.EnabledPermanent || state == VariantState.DisabledPermanent; - bool value = state == VariantState.Enabled || state == VariantState.EnabledTemporary || state == VariantState.EnabledPermanent || (state == VariantState.Toggle && !GetVariantStatus(variant)); - - SetVariantInGame(variant, value); - - if (permanent) { - var session = GrabBagModule.Session; - - Variants[(int)variant] = value; - } - } - - public static bool GetVariantStatus(Variant variant) { - switch (variant) { - case Variant.Hiccups: - return SaveData.Instance.Assists.Hiccups; - case Variant.InfiniteStamina: - return SaveData.Instance.Assists.InfiniteStamina; - case Variant.Invincible: - return SaveData.Instance.Assists.Invincible; - case Variant.InvisibleMotion: - return SaveData.Instance.Assists.InvisibleMotion; - case Variant.LowFriction: - return SaveData.Instance.Assists.LowFriction; - case Variant.MirrorMode: - return SaveData.Instance.Assists.MirrorMode; - case Variant.NoGrabbing: - return SaveData.Instance.Assists.NoGrabbing; - case Variant.PlayAsBadeline: - return SaveData.Instance.Assists.PlayAsBadeline; - case Variant.SuperDashing: - return SaveData.Instance.Assists.SuperDashing; - case Variant.ThreeSixtyDashing: - return SaveData.Instance.Assists.ThreeSixtyDashing; - case Variant.DashAssist: - return SaveData.Instance.Assists.DashAssist; - } - return false; - } - public static bool? GetVariantModdedValue(Variant variant) { - - return Variants[(int)variant]; - } - - private static void SetVariantInGame(Variant variant, bool value) { - // Set value in game - switch (variant) { - case Variant.Hiccups: - SaveData.Instance.Assists.Hiccups = value; - break; - case Variant.InfiniteStamina: - SaveData.Instance.Assists.InfiniteStamina = value; - break; - case Variant.Invincible: - SaveData.Instance.Assists.Invincible = value; - break; - case Variant.InvisibleMotion: - SaveData.Instance.Assists.InvisibleMotion = value; - break; - case Variant.LowFriction: - SaveData.Instance.Assists.LowFriction = value; - break; - case Variant.MirrorMode: - SaveData.Instance.Assists.MirrorMode = Input.Aim.InvertedX = Input.MoveX.Inverted = value; - break; - case Variant.NoGrabbing: - SaveData.Instance.Assists.NoGrabbing = value; - break; - case Variant.PlayAsBadeline: - bool originalValue = SaveData.Instance.Assists.PlayAsBadeline; - SaveData.Instance.Assists.PlayAsBadeline = value; - if (value != originalValue) { - // apply the effect immediately - Player entity = Engine.Scene.Tracker.GetEntity(); - if (entity != null) { - PlayerSpriteMode mode = SaveData.Instance.Assists.PlayAsBadeline ? PlayerSpriteMode.MadelineAsBadeline : entity.DefaultSpriteMode; - if (entity.Active) { - entity.ResetSpriteNextFrame(mode); - return; - } - entity.ResetSprite(mode); - } - } - break; - case Variant.SuperDashing: - SaveData.Instance.Assists.SuperDashing = value; - break; - case Variant.ThreeSixtyDashing: - SaveData.Instance.Assists.ThreeSixtyDashing = value; - break; - case Variant.DashAssist: - SaveData.Instance.Assists.DashAssist = value; - break; - } - } - } - public class ForceVariantTrigger : Trigger - { - public Variant variant; - public VariantState enable; - - private bool previous; - - public ForceVariantTrigger(EntityData data, Vector2 offset) : base(data, offset) - { - variant = data.Enum("variantChange", Variant.Hiccups); - enable = data.Enum("enableStyle", VariantState.Enabled); - - if (data.Has("enforceValue")) { - bool enforce = data.Bool("enforceValue"); - if (enable == VariantState.Enabled) { - enable = VariantState.EnabledTemporary; - } - else if (enable == VariantState.Disabled) { - enable = VariantState.DisabledTemporary; - } - else if (enable == VariantState.EnabledPermanent && !enforce) { - enable = VariantState.Enabled; - } - else if (enable == VariantState.DisabledPermanent) { - enable = VariantState.Disabled; - } - } - } - - public override void OnEnter(Player player) - { - base.OnEnter(player); - - previous = ForceVariants.GetVariantStatus(variant); - ForceVariants.SetVariant(variant, enable); - - } - public override void OnLeave(Player player) - { - base.OnLeave(player); - - switch (enable) - { - case VariantState.DisabledTemporary: - case VariantState.EnabledTemporary: - ForceVariants.SetVariant(variant, previous ? VariantState.Enabled : VariantState.Disabled); - break; - } - } - - } - public class VariantEnforcer : Component - { - public VariantEnforcer() : base(true, false) { } - - private Player player; - - public override void Update() { - player = Entity as Player; - if (player == null) { - return; - } - - //ForceVariants.ReinforceSession(); - //ForceVariantTrigger.Reinforce(); - if (GrabBagModule.GrabBagMeta.WaterBoost) - WaterBoost(); - } - - private void WaterBoost() { - - if (!player.Collidable) - return; - - Vector2 posOffset = player.Position + player.Speed * Engine.DeltaTime * 2; - - bool isInWater = player.CollideCheck(posOffset) || player.CollideCheck(posOffset + Vector2.UnitY * -8f); - - if (!isInWater && player.StateMachine.State == 3 && (player.Speed.Y < 0 || Input.MoveY.Value == -1 || Input.Jump.Check)) { - player.Speed.Y = (Input.MoveY.Value == -1 || Input.Jump.Check) ? -110 : 0; - if (player.Speed.Y < -1) { - player.Speed.X *= 1.1f; - } - } - } - } - -} \ No newline at end of file diff --git a/GrabBagMeta.cs b/GrabBagMeta.cs deleted file mode 100644 index c12d3f6..0000000 --- a/GrabBagMeta.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Celeste.Mod.Meta; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Celeste.Mod.IsaGrabBag { - public class GrabBagWrapperMeta : IMeta { - - public GrabBagMeta IsaGrabBag { get; set; } - } - - public class GrabBagMeta { - - public static GrabBagMeta Default(AreaKey area) { - return new GrabBagMeta() { - WaterBoost = area.LevelSet.StartsWith("SpringCollab2020"), - RoundDreamSpinner = false, - CorrectDemoDash = false, - ReplaceDashWith = "nothing", - }; - } - - public bool WaterBoost { get; set; } - public bool RoundDreamSpinner { get; set; } - public bool CorrectDemoDash { get; set; } - public string ReplaceDashWith { get; set; } - } -} diff --git a/Graphics/Atlases/Gameplay/isafriend/baddyAhorn.png b/Graphics/Atlases/Gameplay/isafriend/baddyAhorn.png new file mode 100644 index 0000000..c58cf18 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/baddyAhorn.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamBorder.png b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamBorder.png new file mode 100644 index 0000000..dd8a4b3 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamBorder.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamSpinner.png b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamSpinner.png new file mode 100644 index 0000000..e67b8b4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/dreamSpinner.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner.png b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner.png new file mode 100644 index 0000000..deb574f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner_fake.png b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner_fake.png new file mode 100644 index 0000000..57ff9e0 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/danger/crystal/fg_dreamspinner_fake.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/helperimage.png b/Graphics/Atlases/Gameplay/isafriend/helperimage.png new file mode 100644 index 0000000..7c56d68 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/helperimage.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block00.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block00.png new file mode 100644 index 0000000..28904aa Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block01.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block01.png new file mode 100644 index 0000000..031ddef Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block02.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block02.png new file mode 100644 index 0000000..b85a51b Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block03.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block03.png new file mode 100644 index 0000000..889c0b1 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block04.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block04.png new file mode 100644 index 0000000..4291400 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/block04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/idle_face.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/idle_face.png new file mode 100644 index 0000000..7f80b7d Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/idle_face.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottom.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottom.png new file mode 100644 index 0000000..1a33bde Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottom.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomleft.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomleft.png new file mode 100644 index 0000000..6c2d94f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomleft.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomright.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomright.png new file mode 100644 index 0000000..6da092a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_bottomright.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_left.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_left.png new file mode 100644 index 0000000..5a0f011 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_left.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_right.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_right.png new file mode 100644 index 0000000..17f978b Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_right.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_top.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_top.png new file mode 100644 index 0000000..dd8fab9 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_top.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topleft.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topleft.png new file mode 100644 index 0000000..2640e07 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topleft.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topright.png b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topright.png new file mode 100644 index 0000000..cb7eb53 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/arrowblock/lit_topright.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown00.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown00.png new file mode 100644 index 0000000..29181d8 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown01.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown01.png new file mode 100644 index 0000000..7b2f0d7 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown02.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown02.png new file mode 100644 index 0000000..a647e00 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown03.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown03.png new file mode 100644 index 0000000..d88b74a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown04.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown04.png new file mode 100644 index 0000000..7b3dafd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown05.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown05.png new file mode 100644 index 0000000..6166358 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown05.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown06.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown06.png new file mode 100644 index 0000000..2f87300 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown06.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown07.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown07.png new file mode 100644 index 0000000..21084f6 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown07.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown08.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown08.png new file mode 100644 index 0000000..da1b002 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown08.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown09.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown09.png new file mode 100644 index 0000000..ef2d0df Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown09.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown10.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown10.png new file mode 100644 index 0000000..b1aac49 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown10.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown11.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown11.png new file mode 100644 index 0000000..3faf20a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown11.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown12.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown12.png new file mode 100644 index 0000000..ef6c023 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown12.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown13.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown13.png new file mode 100644 index 0000000..91ec185 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown13.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown14.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown14.png new file mode 100644 index 0000000..44300a2 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown14.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown15.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown15.png new file mode 100644 index 0000000..601bac4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown15.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown16.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown16.png new file mode 100644 index 0000000..057103c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown16.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown17.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown17.png new file mode 100644 index 0000000..7244b37 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown17.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown18.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown18.png new file mode 100644 index 0000000..6166358 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown18.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown19.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown19.png new file mode 100644 index 0000000..39f1ae4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown19.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown20.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown20.png new file mode 100644 index 0000000..1faac86 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown20.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown21.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown21.png new file mode 100644 index 0000000..f42be40 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown21.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown22.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown22.png new file mode 100644 index 0000000..b5cf775 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown22.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown23.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown23.png new file mode 100644 index 0000000..4bdaffc Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown23.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown24.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown24.png new file mode 100644 index 0000000..54d137e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown24.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown25.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown25.png new file mode 100644 index 0000000..0230fe1 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterdown25.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft00.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft00.png new file mode 100644 index 0000000..6b4dbe2 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft01.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft01.png new file mode 100644 index 0000000..1af4aa7 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft02.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft02.png new file mode 100644 index 0000000..2ef484d Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft03.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft03.png new file mode 100644 index 0000000..c9faa7f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft04.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft04.png new file mode 100644 index 0000000..f57476f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft05.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft05.png new file mode 100644 index 0000000..d7f34e1 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft05.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft06.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft06.png new file mode 100644 index 0000000..e383fb4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft06.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft07.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft07.png new file mode 100644 index 0000000..d5bf962 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft07.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft08.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft08.png new file mode 100644 index 0000000..5a69e75 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft08.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft09.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft09.png new file mode 100644 index 0000000..7436afd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft09.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft10.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft10.png new file mode 100644 index 0000000..0de6194 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft10.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft11.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft11.png new file mode 100644 index 0000000..d82b37a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft11.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft12.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft12.png new file mode 100644 index 0000000..cf2781f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft12.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft13.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft13.png new file mode 100644 index 0000000..8d7cc73 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft13.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft14.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft14.png new file mode 100644 index 0000000..45bdcec Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft14.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft15.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft15.png new file mode 100644 index 0000000..dd98a1a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft15.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft16.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft16.png new file mode 100644 index 0000000..29b9eb5 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft16.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft17.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft17.png new file mode 100644 index 0000000..7244b37 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft17.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft18.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft18.png new file mode 100644 index 0000000..c36721d Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft18.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft19.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft19.png new file mode 100644 index 0000000..e7ab4aa Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft19.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft20.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft20.png new file mode 100644 index 0000000..2bed895 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft20.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft21.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft21.png new file mode 100644 index 0000000..d90dc5b Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft21.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft22.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft22.png new file mode 100644 index 0000000..e923db0 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft22.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft23.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft23.png new file mode 100644 index 0000000..e52fc87 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft23.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft24.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft24.png new file mode 100644 index 0000000..cd6ee0f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft24.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft25.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft25.png new file mode 100644 index 0000000..d831c79 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterleft25.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright00.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright00.png new file mode 100644 index 0000000..1a7964c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright01.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright01.png new file mode 100644 index 0000000..48e3c4b Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright02.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright02.png new file mode 100644 index 0000000..4f88bbd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright03.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright03.png new file mode 100644 index 0000000..5d292df Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright04.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright04.png new file mode 100644 index 0000000..17cb360 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright05.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright05.png new file mode 100644 index 0000000..ab6d1c3 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright05.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright06.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright06.png new file mode 100644 index 0000000..f7b7787 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright06.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright07.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright07.png new file mode 100644 index 0000000..043ab8a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright07.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright08.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright08.png new file mode 100644 index 0000000..ec9ad9f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright08.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright09.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright09.png new file mode 100644 index 0000000..5499738 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright09.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright10.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright10.png new file mode 100644 index 0000000..8b1ead9 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright10.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright11.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright11.png new file mode 100644 index 0000000..8dda33f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright11.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright12.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright12.png new file mode 100644 index 0000000..43b6622 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright12.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright13.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright13.png new file mode 100644 index 0000000..3dc594a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright13.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright14.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright14.png new file mode 100644 index 0000000..75135c5 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright14.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright15.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright15.png new file mode 100644 index 0000000..f6f401c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright15.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright16.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright16.png new file mode 100644 index 0000000..5dc2d9c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright16.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright17.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright17.png new file mode 100644 index 0000000..7244b37 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright17.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright18.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright18.png new file mode 100644 index 0000000..a54c46a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright18.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright19.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright19.png new file mode 100644 index 0000000..ae242e2 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright19.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright20.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright20.png new file mode 100644 index 0000000..241c2fd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright20.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright21.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright21.png new file mode 100644 index 0000000..21a278e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright21.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright22.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright22.png new file mode 100644 index 0000000..706b913 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright22.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright23.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright23.png new file mode 100644 index 0000000..8d95efb Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright23.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright24.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright24.png new file mode 100644 index 0000000..45e2767 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright24.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright25.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright25.png new file mode 100644 index 0000000..bbc0e04 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterright25.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup00.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup00.png new file mode 100644 index 0000000..d968143 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup01.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup01.png new file mode 100644 index 0000000..6406570 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup02.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup02.png new file mode 100644 index 0000000..185c735 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup03.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup03.png new file mode 100644 index 0000000..100fc87 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup04.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup04.png new file mode 100644 index 0000000..a783fe6 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup05.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup05.png new file mode 100644 index 0000000..51dde3e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup05.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup06.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup06.png new file mode 100644 index 0000000..3b65f8a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup06.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup07.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup07.png new file mode 100644 index 0000000..4f9848c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup07.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup08.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup08.png new file mode 100644 index 0000000..cbfafc4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup08.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup09.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup09.png new file mode 100644 index 0000000..49271ef Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup09.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup10.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup10.png new file mode 100644 index 0000000..82e6cfd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup10.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup11.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup11.png new file mode 100644 index 0000000..c933a2f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup11.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup12.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup12.png new file mode 100644 index 0000000..a01471c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup12.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup13.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup13.png new file mode 100644 index 0000000..bb8df39 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup13.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup14.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup14.png new file mode 100644 index 0000000..8d14d3a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup14.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup15.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup15.png new file mode 100644 index 0000000..3a9b3f7 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup15.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup16.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup16.png new file mode 100644 index 0000000..7e5696f Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup16.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup17.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup17.png new file mode 100644 index 0000000..7244b37 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup17.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup18.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup18.png new file mode 100644 index 0000000..4aa7fac Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup18.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup19.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup19.png new file mode 100644 index 0000000..8656ff4 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup19.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup20.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup20.png new file mode 100644 index 0000000..1f0e79e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup20.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup21.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup21.png new file mode 100644 index 0000000..13c785c Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup21.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup22.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup22.png new file mode 100644 index 0000000..8f09dbd Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup22.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup23.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup23.png new file mode 100644 index 0000000..a046b3e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup23.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup24.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup24.png new file mode 100644 index 0000000..d3748c2 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup24.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup25.png b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup25.png new file mode 100644 index 0000000..1bb6c3a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/booster/boosterup25.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle00.png b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle00.png new file mode 100644 index 0000000..3d88dcb Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle01.png b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle01.png new file mode 100644 index 0000000..cb22704 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle02.png b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle02.png new file mode 100644 index 0000000..93336d7 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle03.png b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle03.png new file mode 100644 index 0000000..b2fa120 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle04.png b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle04.png new file mode 100644 index 0000000..cf4e52e Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/pause/idle04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch00.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch00.png new file mode 100644 index 0000000..0116902 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch01.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch01.png new file mode 100644 index 0000000..3a95f39 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch02.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch02.png new file mode 100644 index 0000000..491aab9 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch03.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch03.png new file mode 100644 index 0000000..b908577 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/glitch03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle00.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle00.png new file mode 100644 index 0000000..b376a6a Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle01.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle01.png new file mode 100644 index 0000000..6127630 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle02.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle02.png new file mode 100644 index 0000000..fecac74 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle03.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle03.png new file mode 100644 index 0000000..dcfa7b7 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle03.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle04.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle04.png new file mode 100644 index 0000000..a69f6a1 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/idle04.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/outline00.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/outline00.png new file mode 100644 index 0000000..6f9dd47 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/outline00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine00.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine00.png new file mode 100644 index 0000000..83d4eb2 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine00.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine01.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine01.png new file mode 100644 index 0000000..cf4f25d Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine01.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine02.png b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine02.png new file mode 100644 index 0000000..c5a25ce Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/rewind/shine02.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle.png b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle.png new file mode 100644 index 0000000..a256847 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_end.png b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_end.png new file mode 100644 index 0000000..fa128d5 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_end.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_l.png b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_l.png new file mode 100644 index 0000000..66e7d79 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_l.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_r.png b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_r.png new file mode 100644 index 0000000..7128727 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/objects/zipline/handle_r.png differ diff --git a/Graphics/Atlases/Gameplay/isafriend/tilesets/boost_block.png b/Graphics/Atlases/Gameplay/isafriend/tilesets/boost_block.png new file mode 100644 index 0000000..85eb084 Binary files /dev/null and b/Graphics/Atlases/Gameplay/isafriend/tilesets/boost_block.png differ diff --git a/Graphics/ColorGrading/isagrabbag/rewind_flash.png b/Graphics/ColorGrading/isagrabbag/rewind_flash.png new file mode 100644 index 0000000..7a64135 Binary files /dev/null and b/Graphics/ColorGrading/isagrabbag/rewind_flash.png differ diff --git a/Graphics/IsaGrabBag.xml b/Graphics/IsaGrabBag.xml new file mode 100644 index 0000000..29adfd7 --- /dev/null +++ b/Graphics/IsaGrabBag.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CelesteMod.sln b/IsaGrabBag.sln similarity index 53% rename from CelesteMod.sln rename to IsaGrabBag.sln index 9c70f43..f02cf37 100644 --- a/CelesteMod.sln +++ b/IsaGrabBag.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.106 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32901.215 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CelesteMod", "CelesteMod.csproj", "{BD4DC57E-AB09-4291-8473-AB07471DDAFA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsaGrabBag", "Code\IsaGrabBag.csproj", "{9B1E0548-8AE6-4AAE-9824-90B1A2E97AD0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BD4DC57E-AB09-4291-8473-AB07471DDAFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD4DC57E-AB09-4291-8473-AB07471DDAFA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD4DC57E-AB09-4291-8473-AB07471DDAFA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD4DC57E-AB09-4291-8473-AB07471DDAFA}.Release|Any CPU.Build.0 = Release|Any CPU + {9B1E0548-8AE6-4AAE-9824-90B1A2E97AD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B1E0548-8AE6-4AAE-9824-90B1A2E97AD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B1E0548-8AE6-4AAE-9824-90B1A2E97AD0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B1E0548-8AE6-4AAE-9824-90B1A2E97AD0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3475784B-5F29-4B23-BF9F-25B8437C0B10} + SolutionGuid = {3CE7B6AA-6C2E-499B-B82E-A33002C66B4F} EndGlobalSection EndGlobal diff --git a/IsaSettings.cs b/IsaSettings.cs deleted file mode 100644 index 37637be..0000000 --- a/IsaSettings.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Celeste.Mod.IsaGrabBag -{ - public class IsaSettings : EverestModuleSettings - { - public bool CustomCharacter { get; } = true; - } -} diff --git a/LICENSE.md b/LICENSE similarity index 90% rename from LICENSE.md rename to LICENSE index 356960c..c5baf14 100644 --- a/LICENSE.md +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2022 Isabel S. +Copyright (c) 2022 Isabel S. (original project) +Copyright (c) 2022 Communal Helper Team (all other changes) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Module.cs b/Module.cs deleted file mode 100644 index 8025cc1..0000000 --- a/Module.cs +++ /dev/null @@ -1,303 +0,0 @@ -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Monocle; -using System; - -namespace Celeste.Mod.IsaGrabBag -{ - public class GrabBagModule : EverestModule - { - public static GrabBagMeta GrabBagMeta { - get { - if (gbMeta == null) { - - AreaKey key; - Level level = Engine.Scene as Level; - if (level == null) - level = Engine.NextScene as Level; - - if (level != null) { - key = level.Session.Area; - } - else { - var loader = Engine.Scene as LevelLoader; - key = loader.Level.Session.Area; - } - - gbMeta = GrabBagMeta.Default(key); - }; - return gbMeta; - } - } - private static GrabBagMeta gbMeta; - - public static int ZipLineState { get; private set; } - public static int ArrowBlockState { get; private set; } - - public override Type SessionType => typeof(IsaSession); - public static IsaSession Session => (IsaSession)Instance._Session; - - public override Type SettingsType => typeof(IsaSettings); - public static IsaSettings Settings => (IsaSettings)Instance._Settings; - - public static GrabBagModule Instance { get; private set; } - - public static Player playerInstance { get; private set; } - - public static SpriteBank sprites { get; private set; } - - public GrabBagModule() - { - Instance = this; - } - - public override void Initialize() - { - - base.Initialize(); - } - public override void Load() - { - ArrowBubble.Load(); - DreamSpinnerBorder.OnLoad(); - ForceVariants.Load(); - RewindCrystal.Load(); - - Everest.Events.Level.OnTransitionTo += Level_OnTransitionTo; - - Everest.Events.Level.OnEnter += Level_OnEnter; - Everest.Events.Level.OnExit += Level_OnExit; - Everest.Events.Level.OnLoadEntity += Level_OnLoadEntity; - Everest.Events.Player.OnSpawn += Player_OnSpawn; - - On.Celeste.Player.ctor += PlayerInit; - On.Celeste.Player.UpdateSprite += UpdatePlayerVisuals; - On.Celeste.Player.Update += ZipLine.OnPlayerUpdate; - - On.Celeste.BadelineBoost.Awake += BadelineBoostAwake; - - On.Celeste.ChangeRespawnTrigger.OnEnter += OnChangeRespawn; - } - public override void Unload() { - ArrowBubble.Unload(); - DreamSpinnerBorder.OnUnload(); - ForceVariants.Unload(); - RewindCrystal.Unload(); - - Everest.Events.Level.OnEnter -= Level_OnEnter; - Everest.Events.Level.OnExit -= Level_OnExit; - Everest.Events.Level.OnLoadEntity -= Level_OnLoadEntity; - Everest.Events.Player.OnSpawn -= Player_OnSpawn; - - On.Celeste.Player.ctor -= PlayerInit; - On.Celeste.Player.UpdateSprite -= UpdatePlayerVisuals; - On.Celeste.Player.Update -= ZipLine.OnPlayerUpdate; - - On.Celeste.BadelineBoost.Awake -= BadelineBoostAwake; - - On.Celeste.ChangeRespawnTrigger.OnEnter -= OnChangeRespawn; - } - - private void BadelineBoostAwake(On.Celeste.BadelineBoost.orig_Awake orig, BadelineBoost self, Scene scene) - { - orig(self, scene); - - if ((scene as Level).Session.GetFlag(BadelineFollower.SESSION_FLAG)) - self.Visible = false; - } - - - private void UpdatePlayerVisuals(On.Celeste.Player.orig_UpdateSprite orig, Player self) - { - if (self.StateMachine == ZipLineState) - { - self.Sprite.Scale.X = Calc.Approach(self.Sprite.Scale.X, 1f, 1.75f * Engine.DeltaTime); - self.Sprite.Scale.Y = Calc.Approach(self.Sprite.Scale.Y, 1f, 1.75f * Engine.DeltaTime); - - if (ZipLine.GrabbingCoroutine) - return; - - self.Sprite.PlayOffset("fallSlow_carry", .5f, false); - self.Sprite.Rate = 0.0f; - - } - else - { - orig(self); - } - } - - private void OnChangeRespawn(On.Celeste.ChangeRespawnTrigger.orig_OnEnter orig, ChangeRespawnTrigger self, Player player) - { - orig(self, player); - - ForceVariants.SaveToSession(); - } - - private void PlayerInit(On.Celeste.Player.orig_ctor orig, Player self, Vector2 position, PlayerSpriteMode spriteMode) - { - orig(self, position, spriteMode); - - - ZipLineState = self.StateMachine.AddState(ZipLine.ZipLineUpdate, begin: ZipLine.ZipLineBegin, end: ZipLine.ZipLineEnd, coroutine: ZipLine.ZipLineCoroutine); - } - - public override void LoadContent(bool firstLoad) - { - DreamSpinnerBorder.LoadTextures(); - RewindCrystal.LoadGraphics(); - - sprites = new SpriteBank(GFX.Game, "Graphics/IsaGrabBag.xml"); - - } - - - private void Player_OnSpawn(Player obj) - { - Level lvl = obj.SceneAs(); - - playerInstance = obj; - - ForceVariants.GetFromSession(); - - if (obj.Get() == null) - { - obj.Add(new VariantEnforcer()); - } - - if (lvl.Session.GetFlag(BadelineFollower.SESSION_FLAG)) - { - foreach (BadelineBoost boost in lvl.Entities.FindAll()) - { - boost.Visible = false; - boost.Collidable = false; - } - - if (lvl.Entities.FindFirst() == null) - { - if (BadelineFollower.instance == null) - { - BadelineFollower follower = new BadelineFollower(lvl, obj.Position + new Vector2((int)playerInstance.Facing * -12, -20)); - lvl.Add(follower); - obj.Leader.GainFollower(follower.follower); - } - else - { - BadelineFollower.instance.Readd(lvl, obj); - } - } - } - if (lvl.Session.GetFlag(BadelineFollower.SESSION_FLAG)) - BadelineFollower.instance.dummy.Visible = true; - - BadelineFollower.CheckBooster(lvl, false); - } - - private void Level_OnExit(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) - { - gbMeta = null; - - ForceVariants.ResetSession(); - - if (BadelineFollower.instance != null) - BadelineFollower.instance.RemoveSelf(); - - BadelineFollower.instance = null; - } - - private void Level_OnEnter(Session session, bool fromSaveData) { - try { - GrabBagWrapperMeta parsed; - gbMeta = null; - - //string s = session.Area.GetSID(); - if (session != null && session.Area != null && session.Area.SID != null) { - - var get = Everest.Content.Get(session.Area.SID); - if (get != null && get.TryGetMeta(out parsed)) { - gbMeta = parsed.IsaGrabBag; - } - } - - } - catch (Exception e) { - Logger.Log("IsaGrabBag", "Unable to properly get metadata"); - Logger.LogDetailed(e); - } - - if (gbMeta == null) - gbMeta = GrabBagMeta.Default(session.Area); - - if (session.Area.LevelSet.StartsWith("SpringCollab2020")) { - GrabBagMeta.WaterBoost = true; - } - - ForceVariants.GetDefaults(); - ForceVariants.ReinforceSession(); - - } - - - private void Level_OnTransitionTo(Level level, LevelData next, Vector2 direction) { - ForceVariants.SaveToSession(); - - if (GrabBagMeta == null) - gbMeta = GrabBagMeta.Default(level.Session.Area); - - if (level.Session.GetFlag(BadelineFollower.SESSION_FLAG)) { - if (BadelineFollower.instance == null) { - BadelineFollower follower = new BadelineFollower(level, playerInstance.Position); - level.Add(follower); - playerInstance.Leader.GainFollower(follower.follower); - - BadelineFollower.CheckBooster(level, false); - } - BadelineFollower.Search(); - - if (level.Session.GetFlag(BadelineFollower.SESSION_FLAG)) - BadelineFollower.instance.dummy.Visible = true; - } - } - - private bool Level_OnLoadEntity(Level level, LevelData levelData, Vector2 offset, EntityData entityData) - { - switch (entityData.Name) { - case "isaBag/arrowBlock": - level.Add(new ArrowBlock(entityData, offset)); - return true; - case "CoreHeatWindTrigger": - level.Add(new HotColdWind(entityData, offset)); - return true; - case "isaBag/dreamSpinner": - level.Add(new DreamSpinner(entityData, offset, false)); - return true; - case "isaBag/dreamSpinFake": - level.Add(new DreamSpinner(entityData, offset, true)); - return true; - case "ForceVariantTrigger": - level.Add(new ForceVariantTrigger(entityData, offset)); - return true; - case "isaBag/zipline": - level.Add(new ZipLine(entityData, offset)); - return true; - case "isaBag/waterBoost": - level.Add(new WaterBoostMechanic(entityData)); - return true; - case "isaBag/rewindCrystal": - level.Add(new RewindCrystal(entityData, offset)); - return true; - case "isaBag/pauseCrystal": - level.Add(new UnpauseCrystal(entityData, offset)); - return true; - case "isaBag/arrowBubble": - level.Add(new ArrowBubble(entityData, offset)); - return true; - case "isaBag/baddyFollow": - if (!level.Session.GetFlag(BadelineFollower.SESSION_FLAG)) - BadelineFollower.SpawnBadelineFriendo(level); - return true; - } - return false; - } - } -} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ddcb255 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Isa's Grab Bag + +A helper for Celeste. Originally created by IsaGoodFriend and now maintained by the Communal Helper organization. + +The release build can be downloaded [here](https://gamebanana.com/mods/53645). + +Head to our [issues page](https://github.com/CommunalHelper/IsaGrabBag/issues) to leave a bug report or feature request. \ No newline at end of file diff --git a/RewindCrystal.cs b/RewindCrystal.cs deleted file mode 100644 index 86c2220..0000000 --- a/RewindCrystal.cs +++ /dev/null @@ -1,408 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Monocle; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using System.Collections; - -namespace Celeste.Mod.IsaGrabBag { - struct CharacterState { - public Vector2 pos, scale; - public Facings facing; - public int dashCount; - public Color hairColor; - public Vector2[] hairPosition; - public MTexture texture; - - public Vector2 velocity; - - public CharacterState(Player player) { - pos = player.Position; - scale = player.Sprite.Scale; - facing = player.Facing; - - texture = player.Sprite.Texture; - - dashCount = player.Dashes; - - velocity = player.Speed; - - var hair = player.Hair; - hairColor = hair.Color; - hairPosition = new Vector2[10]; - - for (int i = 0; i < Math.Min(hair.Nodes.Count, hairPosition.Length); ++i) { - hairPosition[i] = hair.Nodes[i]; - } - } - - public void SetOnPlayer(Player player) { - player.NaiveMove(pos - player.Position); - player.Sprite.Scale = scale; - player.Facing = facing; - player.Dashes = dashCount; - - player.Sprite.Texture = texture; - - if (hairPosition == null) - return; - - for (int i = 0; i < Math.Min(player.Hair.Nodes.Count, hairPosition.Length); ++i) { - player.Hair.Nodes[i] = hairPosition[i]; - } - } - public void SetOnPlayerFinal(Player player) { - SetOnPlayer(player); - player.Speed = velocity; - } - } - public class RewindCrystal : Entity { - - private const float RewindCount = 3.5f; - private const int rewindFrames = (int)(RewindCount * 60); - - private static CharacterState[] states = new CharacterState[rewindFrames]; - - public static bool Rewinding { get; internal set; } = false; - - private static float RenderStrength; - - private static int currentFrame; - private static int maxRewind; - - private static RenderTarget2D playerTarget; - - private static Effect glitchEffect; - - public static void ClearRewindBuffer() { - currentFrame = 0; - maxRewind = 0; - states[states.Length - 1] = new CharacterState(); - } - - public static void Load() { - - On.Celeste.PlayerCollider.Check += PlayerCollider_Check; - On.Celeste.Player.Update += Player_Update; - On.Celeste.Player.Render += Player_Render; - Everest.Events.Level.OnLoadLevel += Level_OnLoadLevel; - On.Celeste.Mod.UI.SubHudRenderer.Render += SubHudRenderer_Render; - } - - public static void Unload() { - - On.Celeste.PlayerCollider.Check -= PlayerCollider_Check; - On.Celeste.Player.Update -= Player_Update; - On.Celeste.Player.Render -= Player_Render; - Everest.Events.Level.OnLoadLevel -= Level_OnLoadLevel; - On.Celeste.Mod.UI.SubHudRenderer.Render -= SubHudRenderer_Render; - } - - public static void LoadGraphics() { - playerTarget = new RenderTarget2D(Draw.SpriteBatch.GraphicsDevice, 64, 64); - - try { - - var asset = Everest.Content.Get("Effects/glitchy_effect.cso"); - glitchEffect = new Effect(Draw.SpriteBatch.GraphicsDevice, asset.Data); - } - catch { } - } - - - private static void Player_Render(On.Celeste.Player.orig_Render orig, Player self) { - if (RenderStrength > 0) { - - GameplayRenderer.End(); - - Draw.SpriteBatch.GraphicsDevice.SetRenderTarget(playerTarget); - Draw.SpriteBatch.GraphicsDevice.Clear(Color.Transparent); - - Matrix m = Matrix.CreateTranslation(-(self.X - 32), -(self.Y - 32), 0); - - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, null, m); - - orig(self); - - Draw.SpriteBatch.End(); - - Draw.SpriteBatch.GraphicsDevice.SetRenderTarget(GameplayBuffers.Gameplay); - GameplayRenderer.Begin(); - } - else { - orig(self); - - } - } - - private static void SubHudRenderer_Render(On.Celeste.Mod.UI.SubHudRenderer.orig_Render orig, UI.SubHudRenderer self, Scene scene) { - - - if (MInput.Keyboard.Pressed(Microsoft.Xna.Framework.Input.Keys.Q)) { - LoadGraphics(); - } - - if (scene is Level && RenderStrength > 0) { - Level level = scene as Level; - - Entity e = level.Entities.FindFirst(); - - if (e == null) { - orig(self, scene); - return; - } - - float scale = Math.Min(Engine.ViewWidth / 320.0f, Engine.ViewHeight / 180.0f); - - Vector2 p = e.Position; - - Matrix m = Matrix.CreateTranslation(p.X - 32, p.Y - 32, 0) * level.Camera.Matrix * Matrix.CreateScale(scale); - - if (RenderStrength > 0.1) { - - glitchEffect.Parameters["time"].SetValue(scene.RawTimeActive); - glitchEffect.Parameters["strength"].SetValue(1); - - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, glitchEffect, m); - - } - else { - - ColorGrade.Set(GFX.ColorGrades["isagrabbag/rewind_flash"]); - - Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, ColorGrade.Effect, m); - - } - Draw.SpriteBatch.Draw(playerTarget, Vector2.Zero, Color.White); - - Draw.SpriteBatch.End(); - } - - orig(self, scene); - } - - - private static bool PlayerCollider_Check(On.Celeste.PlayerCollider.orig_Check orig, PlayerCollider self, Player player) { - if (Rewinding) - return false; - else - return orig(self, player); - } - - private static void Level_OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader) { - ClearRewindBuffer(); - Rewinding = false; - } - - private static void Player_Update(On.Celeste.Player.orig_Update orig, Player self) { - if (Rewinding) { - Engine.TimeRate = 1; - orig(self); - Engine.TimeRate = 0; - } - else { - orig(self); - states[currentFrame] = new CharacterState(self); - currentFrame++; - currentFrame %= states.Length; - - if (maxRewind < states.Length) - maxRewind++; - } - } - - - private bool oneUse = false; - private Level level; - private float respawnTime; - - Sprite visuals; - Image outline; - - public RewindCrystal(EntityData data, Vector2 offset) : this(data.Position + offset) { } - - public RewindCrystal(Vector2 position) : base(position) { - visuals = GrabBagModule.sprites.Create("rewind_crystal"); - outline = new Image(GFX.Game["isafriend/objects/rewind/outline00"]); - outline.Position = new Vector2(-8, -8); - outline.Visible = false; - - Collider = new Hitbox(16, 16, -8, -8); - - Add(visuals); - Add(new PlayerCollider(OnPlayer)); - Add(outline); - - - Depth = 1500; - } - - - public override void Added(Scene scene) { - base.Added(scene); - level = SceneAs(); - } - - public override void Update() { - - base.Update(); - } - - private void OnPlayer(Player player) { - if (Rewinding) - return; - - var co = new Coroutine(RewindRoutine()); - co.UseRawDeltaTime = true; - Audio.Play("event:/new_content/game/10_farewell/pinkdiamond_touch", Position); - - Add(co); - } - - private IEnumerator RewindRoutine() { - - Player player = Scene.Tracker.GetEntity(); - Rewinding = true; - player.Collidable = false; - - Celeste.Freeze(0.05f); - Collidable = false; - yield return null; - - visuals.Visible = false; - if (!oneUse) - outline.Visible = true; - level.Shake(); - - player.StateMachine.State = Player.StDummy; - player.ForceCameraUpdate = true; - player.DummyGravity = false; - - Engine.TimeRate = 0; - - float f; - for (f = 0; f < 0.5f; f += Engine.RawDeltaTime) { - - RenderStrength = f * 2; - - if (Input.MenuConfirm.Pressed) { - Input.MenuConfirm.ConsumePress(); - break; - } - yield return null; - } - RenderStrength = 1; - - int stateLen = states.Length; - int lastFrame = currentFrame; - - int unpausedState = (currentFrame + 1) % stateLen; - bool hitPause = false; - - const float rewindLength = 2; - - for (f = 0; f < rewindLength + .15f; f += Engine.RawDeltaTime) { - - if (f < rewindLength && !hitPause) { - - float sampledCurve = 1 - Ease.SineInOut(f / 2); - - int offset = (int)((stateLen - 1) * sampledCurve); - int newFrame = (currentFrame + offset) % stateLen; - - while (lastFrame != newFrame) { - - lastFrame = (lastFrame + stateLen - 1) % stateLen; - - if (lastFrame > currentFrame && maxRewind < stateLen) { - unpausedState = 0; - break; - } - - states[lastFrame].SetOnPlayer(player); - - if (level.CollideCheck(player.Collider.Bounds)) { - unpausedState = lastFrame; - hitPause = true; - f = rewindLength - 0.15f; - break; - } - - } - } - if (f >= rewindLength - 0.15f) { - RenderStrength = Calc.ClampedMap(f, rewindLength - 0.15f, rewindLength + 0.15f, 1, 0);// 1 - ((f - rewindLength) / .15f); - } - - - Vector2 position = level.Camera.Position; - Vector2 cameraTarget = player.CameraTarget; - - level.Camera.Position = position + (cameraTarget - position) * (1f - (float)Math.Pow(0.01f, 0.1)); - - yield return null; - } - - RenderStrength = 0; - states[unpausedState].SetOnPlayerFinal(player); - - yield return 0.1f; - - player.StateMachine.State = Player.StNormal; - Engine.TimeRate = 1; - - Rewinding = false; - - player.Collidable = true; - - maxRewind = 0; - - ClearRewindBuffer(); - if (oneUse) - RemoveSelf(); - else { - Add(new Coroutine(RespawnRoutine())); - } - } - - private IEnumerator RespawnRoutine() { - - - yield return 1.5f; - - visuals.Visible = true; - Collidable = true; - Audio.Play("event:/game/general/diamond_return", Position); - } - } - [Tracked(false)] - public class UnpauseCrystal : Entity { - - Sprite visuals; - - public UnpauseCrystal(EntityData data, Vector2 offset) : this(data.Position + offset) { } - - public UnpauseCrystal(Vector2 position) : base(position) { - visuals = GrabBagModule.sprites.Create("pause_crystal"); - - Collider = new Hitbox(12, 12, -6, -6); - - Add(visuals); - - Depth = 1500; - } - - - public override void Added(Scene scene) { - base.Added(scene); - } - - public override void Update() { - - base.Update(); - } - } -} diff --git a/SaveData.cs b/SaveData.cs deleted file mode 100644 index 921b41b..0000000 --- a/SaveData.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Celeste.Mod.IsaGrabBag -{ - public class IsaSaveData : EverestModuleSaveData - { - public bool[] ColorWall { get; set; } = new bool[] { true, true, true }; - public bool[] ColorWallSave { get; set; } = new bool[] { true, true, true }; - public bool?[] Variants { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null }; - public bool?[] Variants_Save { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null }; - public bool[] Variants_Default { get; set; } = new bool[] { false, false, false, false, false, false, false, false, false, false }; - } -} \ No newline at end of file diff --git a/Session.cs b/Session.cs deleted file mode 100644 index 574f08d..0000000 --- a/Session.cs +++ /dev/null @@ -1,10 +0,0 @@ -using YamlDotNet.Serialization; - -namespace Celeste.Mod.IsaGrabBag -{ - public class IsaSession : EverestModuleSession - { - public bool?[] Variants_Save { get; set; } = new bool?[] { null, null, null, null, null, null, null, null, null, null, null }; - public bool[] ColorWallState { get; set; } = new bool[] { false, false, false }; - } -} \ No newline at end of file diff --git a/WallToggling.cs b/WallToggling.cs deleted file mode 100644 index d42a024..0000000 --- a/WallToggling.cs +++ /dev/null @@ -1,400 +0,0 @@ -using Microsoft.Xna.Framework; -using Monocle; -using System.Collections.Generic; -using Celeste.Mod.OutbackHelper; -using System.Reflection; -using System; - -namespace Celeste.Mod.IsaGrabBag { - public static class WallToggleData { - - static bool[] ColorWallEnabled = new bool[] { false, false, false }; - - - public static bool IsEnabled(int index, bool inverted = false) => ColorWallEnabled[index] != inverted; - public static void SetEnabled(int index, bool value = false) { - - if (ColorWallEnabled[index] != value) { - Toggle(index); - } - } - public static void Toggle(int index) { - ColorWallEnabled[index] = !ColorWallEnabled[index]; - - foreach (ToggleBlock block in Engine.Scene.Entities.FindAll()) { - block.SetState(); - } - } - - public static void GetDefaults() { - - for (int i = 0; i < ColorWallEnabled.Length; i++) { - ColorWallEnabled[i] = false; - } - } - public static void SaveToSession() { - - var session = GrabBagModule.Session; - - for (int i = 0; i < ColorWallEnabled.Length; i++) { - session.ColorWallState[i] = ColorWallEnabled[i]; - } - } - public static void GetFromSession() { - - var session = GrabBagModule.Session; - - for (int i = 0; i < ColorWallEnabled.Length; i++) { - ColorWallEnabled[i] = session.ColorWallState[i]; - } - } - public static void ReinforceSession() { - var session = GrabBagModule.Session; - - for (int i = 0; i < ColorWallEnabled.Length; i++) { - ColorWallEnabled[i] = session.ColorWallState[i]; - - } - } - public static void ResetSession() { - GetDefaults(); - } - - - - public static Player playerInstance { get; private set; } - - } - public class ToggleBlock : Solid { - - public static Color[] BOX_COLORS { get; private set; } = new Color[] { new Color(1, 0.2f, 0.2f), new Color(0.2f, 1, 0.2f), new Color(0.2f, 0.2f, 1) }; - - public int colorValue; - public bool inverted; - - List group; - bool groupLeader; - private Vector2 groupOrigin; - private List pressed, solid, all; - private Color color; - - public ToggleBlock(Vector2 position, float width, float height, bool safe, int _colorVal, bool _startInverted) : base(position, width, height, safe) { - colorValue = _colorVal; - inverted = _startInverted; - switch (colorValue) { - default: - color = Color.Red; - break; - case 1: - color = Color.Green; - break; - case 2: - color = Color.Blue; - break; - } - all = new List(); - pressed = new List(); - solid = new List(); - } - public ToggleBlock(EntityData data, Vector2 offset) : this(data.Position + offset, data.Width, data.Height, true, data.Int("colorValue"), data.Bool("startInvert")) { - - } - - public override void Awake(Scene scene) { - base.Awake(scene); - foreach (StaticMover mover in staticMovers) { - Spikes spikes = mover.Entity as Spikes; - if (spikes != null) { - spikes.VisibleWhenDisabled = true; - spikes.EnabledColor = BOX_COLORS[colorValue] * 3f; - spikes.EnabledColor.A = 1; - spikes.DisabledColor = BOX_COLORS[colorValue] * .5f; - } - } - //WallToggleData.CheckForToggleInstance(scene); - - if (group == null) { - groupLeader = true; - group = new List(); - group.Add(this); - FindInGroup(this); - } - - for (float num5 = base.Left; num5 < base.Right; num5 += 8f) { - for (float num6 = base.Top; num6 < base.Bottom; num6 += 8f) { - bool flag = this.CheckForSame(num5 - 8f, num6); - bool flag2 = this.CheckForSame(num5 + 8f, num6); - bool flag3 = this.CheckForSame(num5, num6 - 8f); - bool flag4 = this.CheckForSame(num5, num6 + 8f); - if (flag && flag2 && flag3 && flag4) { - if (!this.CheckForSame(num5 + 8f, num6 - 8f)) { - this.SetImage(num5, num6, 3, 0); - } - else if (!this.CheckForSame(num5 - 8f, num6 - 8f)) { - this.SetImage(num5, num6, 3, 1); - } - else if (!this.CheckForSame(num5 + 8f, num6 + 8f)) { - this.SetImage(num5, num6, 3, 2); - } - else if (!this.CheckForSame(num5 - 8f, num6 + 8f)) { - this.SetImage(num5, num6, 3, 3); - } - else { - this.SetImage(num5, num6, 1, 1); - } - } - else if (flag && flag2 && !flag3 && flag4) { - this.SetImage(num5, num6, 1, 0); - } - else if (flag && flag2 && flag3 && !flag4) { - this.SetImage(num5, num6, 1, 2); - } - else if (flag && !flag2 && flag3 && flag4) { - this.SetImage(num5, num6, 2, 1); - } - else if (!flag && flag2 && flag3 && flag4) { - this.SetImage(num5, num6, 0, 1); - } - else if (flag && !flag2 && !flag3 && flag4) { - this.SetImage(num5, num6, 2, 0); - } - else if (!flag && flag2 && !flag3 && flag4) { - this.SetImage(num5, num6, 0, 0); - } - else if (flag && !flag2 && flag3 && !flag4) { - this.SetImage(num5, num6, 2, 2); - } - else if (!flag && flag2 && flag3 && !flag4) { - this.SetImage(num5, num6, 0, 2); - } - } - } - - SetState(); - updatedPortals = false; - } - - private static bool updatedPortals; - - public override void Update() { - base.Update(); - - - if (!updatedPortals) { - updatedPortals = true; - //if (Everest.Loader.DependencyLoaded(new EverestModuleMetadata() { Name = "OutbackHelper" })) - // WallToggleData.UpdatePortals(SceneAs()); - } - } - - private void UpdateVisualState() { - if (!this.Collidable) { - base.Depth = 8990; - } - else { - Player entity = base.Scene.Tracker.GetEntity(); - if (entity != null && entity.Top >= base.Bottom - 1f) { - base.Depth = 10; - } - else { - base.Depth = -9990; - } - } - - foreach (StaticMover staticMover in this.staticMovers) { - staticMover.Entity.Depth = base.Depth + 1; - } - - //this.side.Depth = base.Depth + 5; - //this.side.Visible = (this.blockHeight > 0); - //this.occluder.Visible = this.Collidable; - - foreach (Image image in this.solid) { - image.Visible = this.Collidable; - } - foreach (Image image2 in this.pressed) { - image2.Visible = !this.Collidable; - } - - if (this.groupLeader) { - Vector2 scale = new Vector2(1f, 1f); - foreach (ToggleBlock toggleBlock in this.group) { - foreach (Image image3 in toggleBlock.all) { - image3.Scale = scale; - } - foreach (StaticMover staticMover2 in toggleBlock.staticMovers) { - Spikes spikes = staticMover2.Entity as Spikes; - if (spikes != null) { - foreach (Component component in spikes.Components) { - Image image4 = component as Image; - if (image4 != null) { - image4.Scale = scale; - } - } - } - } - } - } - - } - private bool CheckForSame(float x, float y) { - foreach (Entity entity in base.Scene.Entities.FindAll()) { - ToggleBlock cassetteBlock = (ToggleBlock)entity; - if (cassetteBlock.colorValue == colorValue && cassetteBlock.Collider.Collide(new Rectangle((int)x, (int)y, 8, 8))) { - return true; - } - } - return false; - } - private void SetImage(float x, float y, int tx, int ty) { - pressed.Add(CreateImage(x, y, tx, ty, GFX.Game["objects/isatoggleblock/pressed" + colorValue])); - solid.Add(CreateImage(x, y, tx, ty, GFX.Game["objects/isatoggleblock/solid" + colorValue])); - } - private Image CreateImage(float x, float y, int tx, int ty, MTexture tex) { - Vector2 value = new Vector2(x - base.X, y - base.Y); - Image image = new Image(tex.GetSubtexture(tx * 8, ty * 8, 8, 8, null)); - Vector2 vector = this.groupOrigin - this.Position; - image.Origin = vector - value; - image.Position = vector; - image.Color = this.color; - Add(image); - all.Add(image); - return image; - } - private void FindInGroup(ToggleBlock block) { - foreach (Entity entity in base.Scene.Entities.FindAll()) { - ToggleBlock toggleBlock = (ToggleBlock)entity; - if (toggleBlock != this && toggleBlock != block && toggleBlock.colorValue == this.colorValue && (toggleBlock.CollideRect(new Rectangle((int)block.X - 1, (int)block.Y, (int)block.Width + 2, (int)block.Height)) || toggleBlock.CollideRect(new Rectangle((int)block.X, (int)block.Y - 1, (int)block.Width, (int)block.Height + 2))) && !this.group.Contains(toggleBlock)) { - this.group.Add(toggleBlock); - this.FindInGroup(toggleBlock); - toggleBlock.group = this.group; - } - } - } - - public override void Added(Scene scene) { - base.Added(scene); - } - public void SetState() { - Collidable = WallToggleData.IsEnabled(colorValue, inverted); - - if (Collidable) { - EnableStaticMovers(); - } - else { - DisableStaticMovers(); - } - - UpdateVisualState(); - } - - //public override void Render() - //{ - // Draw.Rect(Collider, BOX_COLORS[colorValue] * (WallToggleData.IsEnabled(colorValue, inverted) ? 1 : .5f)); - //} - } - public class ToggleSwitch : CrushBlock { - public int SwitchIndex; - bool hitSide, hitTop; - - Sprite animations; - Image protector, lightImage; - - public ToggleSwitch(Vector2 position, float width, float height, Axes axes, int index, bool chillOut = false) : base(position, width, height, axes, chillOut) { - SwitchIndex = index; - OnDashCollide = NewCollision; - SurfaceSoundIndex = 11; - - switch (SwitchIndex) { - default: - Add(animations = GrabBagModule.sprites.Create("blockred")); - break; - case 1: - Add(animations = GrabBagModule.sprites.Create("blockgreen")); - break; - case 2: - Add(animations = GrabBagModule.sprites.Create("blockblue")); - break; - } - - switch (axes) { - default: - hitSide = hitTop = true; - break; - case Axes.Horizontal: - hitSide = true; - hitTop = false; - Add(protector = new Image(GFX.Game["objects/isatoggleblock/solidtop"])); - break; - case Axes.Vertical: - hitSide = false; - hitTop = true; - Add(protector = new Image(GFX.Game["objects/isatoggleblock/solidside"])); - break; - } - } - - public ToggleSwitch(EntityData data, Vector2 offset) : this(data.Position + offset, 16, 16, data.Enum("dashAxis", Axes.Both), data.Int("colorValue")) { - } - - private DashCollisionResults NewCollision(Player player, Vector2 direction) { - if ((direction.X != 0 && !hitSide) || (direction.Y != 0 && !hitTop)) { - return DashCollisionResults.NormalCollision; - } - - StartShaking(.5f); - - animations.Play((direction.X == 0 ? (direction.Y > 0 ? "hittop" : "hitbottom") : (direction.X > 0 ? "hitleft" : "hitright")), true); - Audio.Play("event:/char/madeline/landing", Center, "surface_index", 12); - - WallToggleData.Toggle(SwitchIndex); - foreach (ToggleBlock block in Scene.Entities.FindAll()) { - block.SetState(); - } - - //if (Everest.Loader.DependencyLoaded(new EverestModuleMetadata() { Name = "OutbackHelper" })) - // WallToggleData.UpdatePortals(SceneAs()); - -#if !USE_REFILLS - if (!player.Inventory.NoRefills) -#endif - player.RefillDash(); - - return DashCollisionResults.Bounce; - } - public override void Added(Scene scene) { - base.Added(scene); - //WallToggleData.CheckForToggleInstance(scene); - } - - public override void Render() { - animations.Render(); - if (!hitSide || !hitTop) - protector.Render(); - //Draw.Rect(Collider, Color.Gray); - //Draw.HollowRect(Collider, ToggleBlock.BOX_COLORS[SwitchIndex]); - } - } - - public class ToggleSwitchTrigger : Trigger { - private int color; - private bool onlyOnce; - private bool enable; - - public ToggleSwitchTrigger(EntityData data, Vector2 offset) : base(data, offset) { - color = data.Int("color"); - onlyOnce = data.Bool("onlyOnce"); - enable = data.Bool("enable"); - } - - public override void OnEnter(Player player) { - base.OnEnter(player); - - WallToggleData.SetEnabled(color, enable); - - if (onlyOnce) { - RemoveSelf(); - } - } - } -} \ No newline at end of file diff --git a/WindAgainstPlayer.cs b/WindAgainstPlayer.cs deleted file mode 100644 index d6920f7..0000000 --- a/WindAgainstPlayer.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Xna.Framework; - -namespace Celeste.Mod.IsaGrabBag -{ -} diff --git a/WindTrigger.cs b/WindTrigger.cs deleted file mode 100644 index ae62f28..0000000 --- a/WindTrigger.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Microsoft.Xna.Framework; -using System.Collections.Generic; - -namespace Celeste.Mod.IsaGrabBag -{ - public class HotColdWind : Trigger - { - public WindController.Patterns windHot, windCold; - private bool freezeDreamBlocks; - private static bool current, dreamBlocksCurrent; - - public HotColdWind(EntityData data, Vector2 offset) : base(data, offset) - { - windHot = data.Enum("patternHot", WindController.Patterns.Up); - windCold = data.Enum("patternCold", WindController.Patterns.Down); - freezeDreamBlocks = data.Bool("freezeDream", false); - } - - public override void OnEnter(Player player) - { - base.OnEnter(player); - - WindController.Patterns currentWind = current ? windHot : windCold; - SetWind(Scene, currentWind); - } - public override void OnStay(Player player) - { - base.OnStay(player); - - if (SceneAs().CoreMode == Session.CoreModes.Hot != current) - { - current = !current; - WindController.Patterns currentWind = current ? windHot : windCold; - - SetWind(Scene, currentWind); - } - } - - public static void SetWind(Monocle.Scene scene, WindController.Patterns currentWind) - { - WindController wind = scene.Entities.FindFirst(); - if (wind == null) - { - wind = new WindController(currentWind); - scene.Add(wind); - return; - } - wind.SetPattern(WindController.Patterns.None); - wind.SetPattern(currentWind); - - //if (_freeze) - //{ - // List dreamblocks = scene.Entities.FindAll(); - - // Player player = scene.Entities.FindFirst(); - // if (player != null) - // { - // (scene as Level).Session.Inventory.DreamDash = current; - // foreach (DreamBlock block in dreamblocks) - // { - // block.Added(scene); - // } - // } - //} - } - } - public class WindAgainstPlayer : Trigger - { - WindController.Patterns left, right; - - public WindAgainstPlayer(EntityData data, Vector2 offset) : base(data, offset) - { - if (data.Attr("strongWind", "false").ToLower() == "true") - { - left = WindController.Patterns.LeftStrong; - right = WindController.Patterns.RightStrong; - } - else - { - left = WindController.Patterns.Left; - right = WindController.Patterns.Right; - } - } - - public override void OnStay(Player player) - { - WindController.Patterns pattern = WindController.Patterns.None; - if (Input.MoveX != 0) - { - HotColdWind.SetWind(Scene, Input.MoveX > 0 ? left : right); - } - } - - } -} \ No newline at end of file diff --git a/ZipLine.cs b/ZipLine.cs deleted file mode 100644 index 929f02b..0000000 --- a/ZipLine.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System; -using System.Reflection; -using Monocle; -using Microsoft.Xna.Framework; -using System.Collections; -using System.Collections.Generic; - -namespace Celeste.Mod.IsaGrabBag -{ - struct RenderRectangle { - public Rectangle rect; - public Color color; - - public RenderRectangle(Rectangle r, Color c) { rect = r; color = c; } - } - public class ZipLineRender : Entity - { - private static readonly Color - darkLine = Calc.HexToColor("9292a9"), - lightLine = Calc.HexToColor("bbc0ce"); - - private readonly ZipLine zipInst; - - private Sprite sprite; - - public ZipLineRender(ZipLine instance) - { - zipInst = instance; - - sprite = GrabBagModule.sprites.Create("zipline"); - sprite.Play("idle"); - - sprite.JustifyOrigin(new Vector2(0.5f, 0.25f)); - - Add(sprite); - - Depth = 500; - } - public override void Update() - { - base.Update(); - } - - List renderList = new List(); - - public override void Render() - { - renderList.Clear(); - - Position = zipInst.Position; - - Rectangle tempRect = new Rectangle((int)zipInst.Left, (int)zipInst.Y, (int)(zipInst.Right - zipInst.Left), 1); - tempRect.Inflate(8, 0); - - renderList.Add(new RenderRectangle(tempRect, darkLine)); - - tempRect.Y -= 1; - - renderList.Add(new RenderRectangle(tempRect, lightLine)); - - int left = tempRect.Left, right = tempRect.Right; - - renderList.Add(new RenderRectangle(new Rectangle(left - 2, (int)Y - 3, 2, 6), darkLine)); - renderList.Add(new RenderRectangle(new Rectangle(right, (int)Y - 3, 2, 6), darkLine)); - - foreach (var rl in renderList) - { - Rectangle r = rl.rect; - r.Inflate(1, 0); - Draw.Rect(r, Color.Black); - r.Inflate(-1, 1); - Draw.Rect(r, Color.Black); - } - foreach (var rl in renderList) - { - Draw.Rect(rl.rect, rl.color); - } - - base.Render(); - - } - } - public class ZipLine : Entity - { - private static void MoveEntityTo(Actor ent, Vector2 position) - { - ent.MoveToX(position.X); - ent.MoveToY(position.Y); - } - - private const int STATE_NORMAL = 0; - private const float ZIP_SPEED = 120f; - private const float ZIP_ACCEL = 190f; - private const float ZIP_TURN = 250f; - - private static ZipLine currentGrabbed, lastGrabbed; - - private float left, right, height; - - public float Left { get { return left; } } - public float Right { get { return right; } } - - private float speed; - private bool grabbed; - - private static float ziplineBuffer; - - private static bool CanGrabZip(ZipLine line){ - if (lastGrabbed != line) - return true; - - return ziplineBuffer <= 0; - } - - private Sprite sprite; - - public static bool GrabbingCoroutine { get { return currentGrabbed != null && !currentGrabbed.grabbed; } } - - public static void OnPlayerUpdate(On.Celeste.Player.orig_Update orig, Player self) - { - orig(self); - - ziplineBuffer = Calc.Approach(ziplineBuffer, 0, Engine.DeltaTime); - - if (!Input.GrabCheck) - ziplineBuffer = 0; - } - - public static void ZipLineBegin() - { - var self = GrabBagModule.playerInstance; - self.Ducking = false; - - self.Speed.Y = 0; - - } - public static void ZipLineEnd() - { - currentGrabbed.grabbed = false; - currentGrabbed = null; - ziplineBuffer = 0.35f; - } - public static int ZipLineUpdate() - { - var self = GrabBagModule.playerInstance; - - if (currentGrabbed == null) - return Player.StNormal; - - if (!currentGrabbed.grabbed) - return GrabBagModule.ZipLineState; - - currentGrabbed.speed = self.Speed.X; - - if (Math.Abs(self.LiftSpeed.X) <= Math.Abs(self.Speed.X)) - { - self.LiftSpeed = self.Speed; - self.LiftSpeedGraceTime = 0.15f; - } - - if (Math.Sign(Input.Aim.Value.X) == -Math.Sign(self.Speed.X)) - self.Speed.X = Calc.Approach(self.Speed.X, Input.Aim.Value.X * ZIP_SPEED, ZIP_TURN * Engine.DeltaTime); - else if (Math.Abs(self.Speed.X) <= ZIP_SPEED || Math.Sign(Input.Aim.Value.X) != Math.Sign(self.Speed.X)) - self.Speed.X = Calc.Approach(self.Speed.X, Input.Aim.Value.X * ZIP_SPEED, ZIP_ACCEL * Engine.DeltaTime); - - if (!Input.GrabCheck || self.Stamina <= 0) - { - return STATE_NORMAL; - } - if (Input.Jump.Pressed) - { - Input.Jump.ConsumePress(); - - self.Stamina -= 110f / 8f; - - self.Speed.X *= 0.1f; - - self.Jump(false, true); - - self.LiftSpeed *= 0.4f; - //self.ResetLiftSpeed(); - - - currentGrabbed.speed = Calc.Approach(currentGrabbed.speed, 0, 20); - - return STATE_NORMAL; - } - if (self.CanDash) - { - - self.StartDash(); - return 2; - } - - self.Stamina -= 5 * Engine.DeltaTime; - - return GrabBagModule.ZipLineState; - } - public static IEnumerator ZipLineCoroutine() - { - var self = GrabBagModule.playerInstance; - var speed = self.Speed; - self.Speed = Vector2.Zero; - currentGrabbed.speed = 0; - - self.Sprite.Play("pickup"); - - self.Play("event:/char/madeline/crystaltheo_lift", null, 0f); - - Vector2 playerLerp = new Vector2((self.X + currentGrabbed.X) / 2f, currentGrabbed.Y + 22); - - playerLerp.X = MathHelper.Clamp(playerLerp.X, currentGrabbed.left, currentGrabbed.right); - Vector2 zipLerp = new Vector2(playerLerp.X, currentGrabbed.Y); - - Vector2 playerInit = self.Position, - zipInit = currentGrabbed.Position; - - var tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.Linear, 0.07f, true); - - while (tween.Active) - { - tween.Update(); - - MoveEntityTo(self, Vector2.Lerp(playerInit, playerLerp, tween.Percent)); - currentGrabbed.Position = Vector2.Lerp(zipInit, zipLerp, tween.Percent); - - yield return null; - } - - currentGrabbed.grabbed = true; - - self.Speed = speed; - - MoveEntityTo(self, playerLerp); - currentGrabbed.Position = zipLerp; - - yield break; - } - - public ZipLine(EntityData _data, Vector2 offset) : base(_data.Position + offset) - { - left = X; - right = X; - foreach (var node in _data.Nodes) - { - left = Math.Min(node.X + offset.X, left); - right = Math.Max(node.X + offset.X, right); - } - - height = (_data.Position + offset).Y; - - Collider = new Hitbox(20, 16, -10, 1); - - currentGrabbed = null; - - Depth = -500; - - sprite = GrabBagModule.sprites.Create("zipline"); - sprite.Play("idle"); - - sprite.JustifyOrigin(new Vector2(0.5f, 0.25f)); - - Add(sprite); - } - - public override void Added(Scene scene) - { - base.Added(scene); - scene.Add(new ZipLineRender(this)); - } - - public override void Update() - { - base.Update(); - var player = GrabBagModule.playerInstance; - - if (player == null || player.Dead) - return; - - if (grabbed) - { - if (player.Speed.X > 20) - { - player.LiftSpeed = player.Speed; - player.LiftSpeedGraceTime = 0.2f; - } - if ((player.CenterX > right || player.CenterX < left)) - { - player.Speed.X = 0; - } - player.CenterX = MathHelper.Clamp(player.CenterX, left, right); - Position.X = player.CenterX; - - Position.Y = height; - } - else - { - if (currentGrabbed == null && player != null && !player.Dead && player.CanUnDuck && Input.GrabCheck && CanGrabZip(this)) - { - PropertyInfo info = typeof(Player).GetProperty("IsTired", BindingFlags.NonPublic | BindingFlags.Instance); - - if (player.CollideCheck(this) && (!(bool)info.GetValue(player))) - { - currentGrabbed = this; - lastGrabbed = currentGrabbed; - - player.StateMachine.State = GrabBagModule.ZipLineState; - - } - } - - Position.X += speed * Engine.DeltaTime; - - Position.X = MathHelper.Clamp(Position.X, left, right); - Position.Y = height; - } - - - } - - - - public override void Render() - { - if (grabbed) - { - sprite.Visible = true; - sprite.Play(GrabBagModule.playerInstance.Facing == Facings.Left ? "held_l" : "held_r"); - } - else - { - sprite.Visible = false; - } - - base.Render(); - } - } -} diff --git a/everest.yaml b/everest.yaml new file mode 100644 index 0000000..6b0a3f9 --- /dev/null +++ b/everest.yaml @@ -0,0 +1,9 @@ +- Name: IsaGrabBag + Version: 1.6.14 + DLL: Code/bin/IsaMods.dll + Dependencies: + - Name: Everest + Version: 1.3847.0 + OptionalDependencies: + - Name: BingoUI + Version: 1.2.6 \ No newline at end of file