├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── osu.Game.Rulesets.Bosu.Tests ├── RulesetTestScene.cs ├── TestSceneCherryPiece.cs ├── TestSceneCollision.cs ├── TestSceneDeathOverlay.cs ├── TestSceneEditor.cs ├── TestSceneOsuGame.cs ├── TestSceneOsuPlayer.cs ├── VisualTestRunner.cs └── osu.Game.Rulesets.Bosu.Tests.csproj ├── osu.Game.Rulesets.Bosu.sln ├── osu.Game.Rulesets.Bosu.sln.DotSettings └── osu.Game.Rulesets.Bosu ├── Beatmaps ├── BosuBeatmap.cs └── BosuBeatmapConverter.cs ├── BosuInputManager.cs ├── BosuRuleset.cs ├── Configuration └── BosuRulesetConfigManager.cs ├── Difficulty └── BosuDifficultyCalculator.cs ├── Edit ├── Blueprints │ ├── AngledCherrySelectionBlueprint.cs │ ├── BosuSelectionHandler.cs │ └── InstantCherrySelectionBlueprint.cs ├── BosuBlueprintContainer.cs ├── BosuHitObjectComposer.cs ├── EditorBosuPlayfield.cs ├── EditorDrawableBosuRuleset.cs └── UI │ ├── EditorCherry.cs │ └── EditorCherryPath.cs ├── Extensions ├── CherryTransformableExtensions.cs ├── ConversionExtensions.cs ├── IWannaExtensions.cs └── MathExtensions.cs ├── Judgements └── BosuJudgement.cs ├── Mods ├── BosuModAutoplay.cs ├── BosuModBarrelRoll.cs ├── BosuModDaycore.cs ├── BosuModDoubleTime.cs ├── BosuModEasy.cs ├── BosuModHalfTime.cs ├── BosuModHidden.cs ├── BosuModNightcore.cs ├── BosuModNoFail.cs ├── BosuModSuddenDeath.cs └── BosuModZoomIn.cs ├── MusicHelpers └── CurrentBeatmapProvider.cs ├── Objects ├── AngledCherry.cs ├── BosuHitObject.cs ├── Cherry.cs ├── Drawables │ ├── DrawableAngledCherry.cs │ ├── DrawableBosuHitObject.cs │ ├── DrawableCherry.cs │ ├── DrawableInstantCherry.cs │ └── Pieces │ │ ├── BeatSyncedSprite.cs │ │ └── CherryPiece.cs └── InstantCherry.cs ├── Replays ├── BosuAutoGenerator.cs ├── BosuFramedReplayInputHandler.cs ├── BosuReplayFrame.cs └── BosuReplayRecorder.cs ├── Resources ├── Samples │ ├── death.wav │ ├── double-jump.wav │ ├── entering.wav │ ├── jump.wav │ └── shoot.wav └── Textures │ ├── Cherry │ ├── cherry-base-1.png │ ├── cherry-base-2.png │ ├── cherry-flash-1.png │ ├── cherry-flash-2.png │ ├── cherry-overlay-1.png │ └── cherry-overlay-2.png │ ├── Player │ ├── player_fall_0.png │ ├── player_fall_1.png │ ├── player_idle_0.png │ ├── player_idle_1.png │ ├── player_idle_2.png │ ├── player_idle_3.png │ ├── player_jump_0.png │ ├── player_jump_1.png │ ├── player_run_0.png │ ├── player_run_1.png │ ├── player_run_2.png │ ├── player_run_3.png │ └── player_run_4.png │ ├── Playfield │ ├── corner.png │ └── pipe.png │ ├── bullet.png │ ├── death-circle.png │ ├── death-particle-white.png │ ├── death-particle.png │ ├── game-over.png │ └── logo.png ├── Scoring └── BosuHealthProcessor.cs ├── UI ├── BosuPlayfield.cs ├── BosuPlayfieldAdjustmentContainer.cs ├── BosuPlayfieldBorder.cs ├── BosuSettingsSubsection.cs ├── Death │ ├── DeathOverlay.cs │ ├── DeathParticle.cs │ └── LetterboxOverlay.cs ├── DrawableBosuRuleset.cs ├── Entering │ ├── BeatmapBackgroundSprite.cs │ ├── BeatmapCard.cs │ └── EnteringOverlay.cs ├── Player │ ├── AnimatedPlayerSprite.cs │ ├── BosuPlayer.cs │ └── BulletsContainer.cs └── PlayfieldBackground.cs └── osu.Game.Rulesets.Bosu.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://editorconfig.org 2 | root = true 3 | 4 | [*.cs] 5 | end_of_line = crlf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | #Roslyn naming styles 12 | 13 | #PascalCase for public and protected members 14 | dotnet_naming_style.pascalcase.capitalization = pascal_case 15 | dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected 16 | dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event 17 | dotnet_naming_rule.public_members_pascalcase.severity = error 18 | dotnet_naming_rule.public_members_pascalcase.symbols = public_members 19 | dotnet_naming_rule.public_members_pascalcase.style = pascalcase 20 | 21 | #camelCase for private members 22 | dotnet_naming_style.camelcase.capitalization = camel_case 23 | 24 | dotnet_naming_symbols.private_members.applicable_accessibilities = private 25 | dotnet_naming_symbols.private_members.applicable_kinds = property,method,field,event 26 | dotnet_naming_rule.private_members_camelcase.severity = warning 27 | dotnet_naming_rule.private_members_camelcase.symbols = private_members 28 | dotnet_naming_rule.private_members_camelcase.style = camelcase 29 | 30 | dotnet_naming_symbols.local_function.applicable_kinds = local_function 31 | dotnet_naming_rule.local_function_camelcase.severity = warning 32 | dotnet_naming_rule.local_function_camelcase.symbols = local_function 33 | dotnet_naming_rule.local_function_camelcase.style = camelcase 34 | 35 | #all_lower for private and local constants/static readonlys 36 | dotnet_naming_style.all_lower.capitalization = all_lower 37 | dotnet_naming_style.all_lower.word_separator = _ 38 | 39 | dotnet_naming_symbols.private_constants.applicable_accessibilities = private 40 | dotnet_naming_symbols.private_constants.required_modifiers = const 41 | dotnet_naming_symbols.private_constants.applicable_kinds = field 42 | dotnet_naming_rule.private_const_all_lower.severity = warning 43 | dotnet_naming_rule.private_const_all_lower.symbols = private_constants 44 | dotnet_naming_rule.private_const_all_lower.style = all_lower 45 | 46 | dotnet_naming_symbols.private_static_readonly.applicable_accessibilities = private 47 | dotnet_naming_symbols.private_static_readonly.required_modifiers = static,readonly 48 | dotnet_naming_symbols.private_static_readonly.applicable_kinds = field 49 | dotnet_naming_rule.private_static_readonly_all_lower.severity = warning 50 | dotnet_naming_rule.private_static_readonly_all_lower.symbols = private_static_readonly 51 | dotnet_naming_rule.private_static_readonly_all_lower.style = all_lower 52 | 53 | dotnet_naming_symbols.local_constants.applicable_kinds = local 54 | dotnet_naming_symbols.local_constants.required_modifiers = const 55 | dotnet_naming_rule.local_const_all_lower.severity = warning 56 | dotnet_naming_rule.local_const_all_lower.symbols = local_constants 57 | dotnet_naming_rule.local_const_all_lower.style = all_lower 58 | 59 | #ALL_UPPER for non private constants/static readonlys 60 | dotnet_naming_style.all_upper.capitalization = all_upper 61 | dotnet_naming_style.all_upper.word_separator = _ 62 | 63 | dotnet_naming_symbols.public_constants.applicable_accessibilities = public,internal,protected,protected_internal,private_protected 64 | dotnet_naming_symbols.public_constants.required_modifiers = const 65 | dotnet_naming_symbols.public_constants.applicable_kinds = field 66 | dotnet_naming_rule.public_const_all_upper.severity = warning 67 | dotnet_naming_rule.public_const_all_upper.symbols = public_constants 68 | dotnet_naming_rule.public_const_all_upper.style = all_upper 69 | 70 | dotnet_naming_symbols.public_static_readonly.applicable_accessibilities = public,internal,protected,protected_internal,private_protected 71 | dotnet_naming_symbols.public_static_readonly.required_modifiers = static,readonly 72 | dotnet_naming_symbols.public_static_readonly.applicable_kinds = field 73 | dotnet_naming_rule.public_static_readonly_all_upper.severity = warning 74 | dotnet_naming_rule.public_static_readonly_all_upper.symbols = public_static_readonly 75 | dotnet_naming_rule.public_static_readonly_all_upper.style = all_upper 76 | 77 | #Roslyn formating options 78 | 79 | #Formatting - indentation options 80 | csharp_indent_case_contents = true 81 | csharp_indent_case_contents_when_block = false 82 | csharp_indent_labels = one_less_than_current 83 | csharp_indent_switch_labels = true 84 | 85 | #Formatting - new line options 86 | csharp_new_line_before_catch = true 87 | csharp_new_line_before_else = true 88 | csharp_new_line_before_finally = true 89 | csharp_new_line_before_open_brace = all 90 | #csharp_new_line_before_members_in_anonymous_types = true 91 | #csharp_new_line_before_members_in_object_initializers = true # Currently no effect in VS/dotnet format (16.4), and makes Rider confusing 92 | csharp_new_line_between_query_expression_clauses = true 93 | 94 | #Formatting - organize using options 95 | dotnet_sort_system_directives_first = true 96 | 97 | #Formatting - spacing options 98 | csharp_space_after_cast = false 99 | csharp_space_after_colon_in_inheritance_clause = true 100 | csharp_space_after_keywords_in_control_flow_statements = true 101 | csharp_space_before_colon_in_inheritance_clause = true 102 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 103 | csharp_space_between_method_call_name_and_opening_parenthesis = false 104 | csharp_space_between_method_call_parameter_list_parentheses = false 105 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 106 | csharp_space_between_method_declaration_parameter_list_parentheses = false 107 | 108 | #Formatting - wrapping options 109 | csharp_preserve_single_line_blocks = true 110 | csharp_preserve_single_line_statements = true 111 | 112 | #Roslyn language styles 113 | 114 | #Style - this. qualification 115 | dotnet_style_qualification_for_field = false:warning 116 | dotnet_style_qualification_for_property = false:warning 117 | dotnet_style_qualification_for_method = false:warning 118 | dotnet_style_qualification_for_event = false:warning 119 | 120 | #Style - type names 121 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 122 | dotnet_style_predefined_type_for_member_access = true:warning 123 | csharp_style_var_when_type_is_apparent = true:none 124 | csharp_style_var_for_built_in_types = true:none 125 | csharp_style_var_elsewhere = true:silent 126 | 127 | #Style - modifiers 128 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning 129 | csharp_preferred_modifier_order = public,private,protected,internal,new,abstract,virtual,sealed,override,static,readonly,extern,unsafe,volatile,async:warning 130 | 131 | #Style - parentheses 132 | # Skipped because roslyn cannot separate +-*/ with << >> 133 | 134 | #Style - expression bodies 135 | csharp_style_expression_bodied_accessors = true:warning 136 | csharp_style_expression_bodied_constructors = false:none 137 | csharp_style_expression_bodied_indexers = true:warning 138 | csharp_style_expression_bodied_methods = false:silent 139 | csharp_style_expression_bodied_operators = true:warning 140 | csharp_style_expression_bodied_properties = true:warning 141 | csharp_style_expression_bodied_local_functions = true:silent 142 | 143 | #Style - expression preferences 144 | dotnet_style_object_initializer = true:warning 145 | dotnet_style_collection_initializer = true:warning 146 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning 147 | dotnet_style_prefer_auto_properties = true:warning 148 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 149 | dotnet_style_prefer_conditional_expression_over_return = true:silent 150 | dotnet_style_prefer_compound_assignment = true:warning 151 | 152 | #Style - null/type checks 153 | dotnet_style_coalesce_expression = true:warning 154 | dotnet_style_null_propagation = true:warning 155 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 156 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 157 | csharp_style_throw_expression = true:silent 158 | csharp_style_conditional_delegate_call = true:warning 159 | 160 | #Style - unused 161 | dotnet_style_readonly_field = true:silent 162 | dotnet_code_quality_unused_parameters = non_public:silent 163 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 164 | csharp_style_unused_value_assignment_preference = discard_variable:warning 165 | 166 | #Style - variable declaration 167 | csharp_style_inlined_variable_declaration = true:warning 168 | csharp_style_deconstructed_variable_declaration = true:warning 169 | 170 | #Style - other C# 7.x features 171 | dotnet_style_prefer_inferred_tuple_names = true:warning 172 | csharp_prefer_simple_default_expression = true:warning 173 | csharp_style_pattern_local_over_anonymous_function = true:warning 174 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent 175 | 176 | #Style - C# 8 features 177 | csharp_prefer_static_local_function = true:warning 178 | csharp_prefer_simple_using_statement = true:silent 179 | csharp_style_prefer_index_operator = true:warning 180 | csharp_style_prefer_range_operator = true:warning 181 | csharp_style_prefer_switch_expression = false:none 182 | 183 | #Supressing roslyn built-in analyzers 184 | # Suppress: EC112 185 | 186 | #Private method is unused 187 | dotnet_diagnostic.IDE0051.severity = silent 188 | #Private member is unused 189 | dotnet_diagnostic.IDE0052.severity = silent 190 | 191 | #Rules for disposable 192 | dotnet_diagnostic.IDE0067.severity = none 193 | dotnet_diagnostic.IDE0068.severity = none 194 | dotnet_diagnostic.IDE0069.severity = none 195 | 196 | #Disable operator overloads requiring alternate named methods 197 | dotnet_diagnostic.CA2225.severity = none -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: evast 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andrei Zavatski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bosu 2 | A custom game mode for osu!lazer project based on "I wanna be the Boshy" game. 3 | 4 | Support: 5 | 6 | https://www.patreon.com/evast 7 | 8 | https://boosty.to/evast 9 | 10 | Follow me: 11 | 12 | https://discord.gg/d2ZCzC42yH 13 | 14 | https://youtube.com/c/evast_osu 15 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/RulesetTestScene.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Tests.Visual; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Tests 4 | { 5 | public abstract partial class RulesetTestScene : OsuTestScene 6 | { 7 | protected override Ruleset CreateRuleset() => new BosuRuleset(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneCherryPiece.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Bosu.Objects.Drawables.Pieces; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Testing; 4 | using osuTK; 5 | using osuTK.Graphics; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Tests 8 | { 9 | public partial class TestSceneCherryPiece : RulesetTestScene 10 | { 11 | private const int count = 40; 12 | 13 | private float red; 14 | private float green; 15 | private float blue; 16 | 17 | public TestSceneCherryPiece() 18 | { 19 | for (int i = 0; i < count; i++) 20 | { 21 | for (int j = 0; j < count; j++) 22 | { 23 | Add(new CherryPiece 24 | { 25 | Origin = Anchor.Centre, 26 | RelativePositionAxes = Axes.Both, 27 | Position = new Vector2((float)i / count, (float)j / count) + new Vector2(1f / count) * 0.5f 28 | }); 29 | } 30 | } 31 | } 32 | 33 | protected override void LoadComplete() 34 | { 35 | base.LoadComplete(); 36 | 37 | AddSliderStep("Red", 0f, 1f, 1f, r => 38 | { 39 | red = r; 40 | updateColour(); 41 | }); 42 | AddSliderStep("Green", 0f, 1f, 1f, g => 43 | { 44 | green = g; 45 | updateColour(); 46 | }); 47 | AddSliderStep("Blue", 0f, 1f, 1f, b => 48 | { 49 | blue = b; 50 | updateColour(); 51 | }); 52 | AddStep("Flash", () => 53 | { 54 | foreach (var c in this.ChildrenOfType()) 55 | { 56 | c.Flash(200); 57 | } 58 | }); 59 | } 60 | 61 | private void updateColour() 62 | { 63 | foreach (var c in this.ChildrenOfType()) 64 | { 65 | c.CherryColour = new Color4(red, green, blue, 1); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneCollision.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Game.Tests.Visual; 3 | using osu.Framework.Graphics; 4 | using osuTK; 5 | using osu.Framework.Graphics.Shapes; 6 | using osuTK.Graphics; 7 | using osu.Game.Rulesets.Bosu.Extensions; 8 | 9 | namespace osu.Game.Rulesets.Bosu.Tests 10 | { 11 | public partial class TestSceneCollision : OsuTestScene 12 | { 13 | private const int radius = 50; 14 | 15 | private readonly Circle circle; 16 | private readonly Box box; 17 | private readonly Container container; 18 | 19 | public TestSceneCollision() 20 | { 21 | Add(container = new Container 22 | { 23 | Anchor = Anchor.Centre, 24 | Origin = Anchor.Centre, 25 | Size = new Vector2(600), 26 | Children = new Drawable[] 27 | { 28 | new Box 29 | { 30 | RelativeSizeAxes = Axes.Both, 31 | Colour = Color4.Black 32 | }, 33 | box = new Box 34 | { 35 | Position = new Vector2(100), 36 | Size = new Vector2(75, 200), 37 | }, 38 | circle = new Circle 39 | { 40 | Size = new Vector2(radius * 2), 41 | Origin = Anchor.Centre, 42 | } 43 | } 44 | }); 45 | } 46 | 47 | protected override void Update() 48 | { 49 | base.Update(); 50 | 51 | circle.Position = container.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); 52 | circle.Colour = MathExtensions.Collided(radius, circle.Position, box.Position, box.Size) ? Color4.Red : Color4.White; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneDeathOverlay.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osu.Game.Rulesets.Bosu.UI; 4 | using osu.Game.Rulesets.Bosu.UI.Death; 5 | using osuTK; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Tests 8 | { 9 | public partial class TestSceneDeathOverlay : RulesetTestScene 10 | { 11 | private Container content; 12 | private DeathOverlay deathOverlay; 13 | 14 | public TestSceneDeathOverlay() 15 | { 16 | Add(content = new Container 17 | { 18 | Anchor = Anchor.Centre, 19 | Origin = Anchor.Centre, 20 | Size = BosuPlayfield.BASE_SIZE 21 | }); 22 | } 23 | 24 | protected override void LoadComplete() 25 | { 26 | base.LoadComplete(); 27 | 28 | AddStep("Trigger death", () => 29 | { 30 | content.Clear(); 31 | content.Add(deathOverlay = new DeathOverlay()); 32 | deathOverlay.Show(new Vector2(BosuPlayfield.BASE_SIZE.X / 2f, BosuPlayfield.BASE_SIZE.Y / 2f), new Vector2(3, 0)); 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneEditor.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using osu.Game.Tests.Visual; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Tests 5 | { 6 | [TestFixture] 7 | public partial class TestSceneEditor : EditorTestScene 8 | { 9 | protected override Ruleset CreateEditorRuleset() => new BosuRuleset(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneOsuGame.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Platform; 3 | using osu.Game.Tests.Visual; 4 | 5 | namespace osu.Game.Rulesets.Bosu.Tests 6 | { 7 | public partial class TestSceneOsuGame : OsuTestScene 8 | { 9 | [BackgroundDependencyLoader] 10 | private void load(GameHost host) 11 | { 12 | OsuGame game = new OsuGame(); 13 | game.SetHost(host); 14 | 15 | AddGame(game); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/TestSceneOsuPlayer.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using osu.Game.Tests.Visual; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Tests 5 | { 6 | [TestFixture] 7 | public partial class TestSceneOsuPlayer : PlayerTestScene 8 | { 9 | protected override Ruleset CreatePlayerRuleset() => new BosuRuleset(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/VisualTestRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using osu.Framework; 3 | using osu.Framework.Platform; 4 | using osu.Game.Tests; 5 | 6 | namespace osu.Game.Rulesets.Bosu.Tests 7 | { 8 | public static class VisualTestRunner 9 | { 10 | [STAThread] 11 | public static int Main(string[] args) 12 | { 13 | using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu")) 14 | { 15 | host.Run(new OsuTestBrowser()); 16 | return 0; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.Tests/osu.Game.Rulesets.Bosu.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | osu.Game.Rulesets.Bosu.Tests.VisualTestRunner 4 | 8 5 | 6 | 7 | 8 | 9 | 10 | WinExe 11 | net8.0 12 | osu.Game.Rulesets.Bosu.Tests 13 | 14 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29123.88 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Bosu", "osu.Game.Rulesets.Bosu\osu.Game.Rulesets.Bosu.csproj", "{5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Bosu.Tests", "osu.Game.Rulesets.Bosu.Tests\osu.Game.Rulesets.Bosu.Tests.csproj", "{B4577C85-CB83-462A-BCE3-22FFEB16311D}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | VisualTests|Any CPU = VisualTests|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.VisualTests|Any CPU.ActiveCfg = Release|Any CPU 22 | {5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}.VisualTests|Any CPU.Build.0 = Release|Any CPU 23 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.VisualTests|Any CPU.ActiveCfg = Debug|Any CPU 28 | {B4577C85-CB83-462A-BCE3-22FFEB16311D}.VisualTests|Any CPU.Build.0 = Debug|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(NestedProjects) = preSolution 34 | EndGlobalSection 35 | GlobalSection(ExtensibilityGlobals) = postSolution 36 | SolutionGuid = {671B0BEC-2403-45B0-9357-2C97CC517668} 37 | EndGlobalSection 38 | GlobalSection(MonoDevelopProperties) = preSolution 39 | Policies = $0 40 | $0.TextStylePolicy = $1 41 | $1.EolMarker = Windows 42 | $1.inheritsSet = VisualStudio 43 | $1.inheritsScope = text/plain 44 | $1.scope = text/x-csharp 45 | $0.CSharpFormattingPolicy = $2 46 | $2.IndentSwitchSection = True 47 | $2.NewLinesForBracesInProperties = True 48 | $2.NewLinesForBracesInAccessors = True 49 | $2.NewLinesForBracesInAnonymousMethods = True 50 | $2.NewLinesForBracesInControlBlocks = True 51 | $2.NewLinesForBracesInAnonymousTypes = True 52 | $2.NewLinesForBracesInObjectCollectionArrayInitializers = True 53 | $2.NewLinesForBracesInLambdaExpressionBody = True 54 | $2.NewLineForElse = True 55 | $2.NewLineForCatch = True 56 | $2.NewLineForFinally = True 57 | $2.NewLineForMembersInObjectInit = True 58 | $2.NewLineForMembersInAnonymousTypes = True 59 | $2.NewLineForClausesInQuery = True 60 | $2.SpacingAfterMethodDeclarationName = False 61 | $2.SpaceAfterMethodCallName = False 62 | $2.SpaceBeforeOpenSquareBracket = False 63 | $2.inheritsSet = Mono 64 | $2.inheritsScope = text/x-csharp 65 | $2.scope = text/x-csharp 66 | EndGlobalSection 67 | GlobalSection(MonoDevelopProperties) = preSolution 68 | Policies = $0 69 | $0.TextStylePolicy = $1 70 | $1.EolMarker = Windows 71 | $1.inheritsSet = VisualStudio 72 | $1.inheritsScope = text/plain 73 | $1.scope = text/x-csharp 74 | $0.CSharpFormattingPolicy = $2 75 | $2.IndentSwitchSection = True 76 | $2.NewLinesForBracesInProperties = True 77 | $2.NewLinesForBracesInAccessors = True 78 | $2.NewLinesForBracesInAnonymousMethods = True 79 | $2.NewLinesForBracesInControlBlocks = True 80 | $2.NewLinesForBracesInAnonymousTypes = True 81 | $2.NewLinesForBracesInObjectCollectionArrayInitializers = True 82 | $2.NewLinesForBracesInLambdaExpressionBody = True 83 | $2.NewLineForElse = True 84 | $2.NewLineForCatch = True 85 | $2.NewLineForFinally = True 86 | $2.NewLineForMembersInObjectInit = True 87 | $2.NewLineForMembersInAnonymousTypes = True 88 | $2.NewLineForClausesInQuery = True 89 | $2.SpacingAfterMethodDeclarationName = False 90 | $2.SpaceAfterMethodCallName = False 91 | $2.SpaceBeforeOpenSquareBracket = False 92 | $2.inheritsSet = Mono 93 | $2.inheritsScope = text/x-csharp 94 | $2.scope = text/x-csharp 95 | EndGlobalSection 96 | EndGlobal 97 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Beatmaps/BosuBeatmap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using osu.Game.Beatmaps; 4 | using osu.Game.Rulesets.Bosu.Objects; 5 | 6 | namespace osu.Game.Rulesets.Bosu.Beatmaps 7 | { 8 | public class BosuBeatmap : Beatmap 9 | { 10 | public override IEnumerable GetStatistics() 11 | { 12 | var totalCount = HitObjects.Count(); 13 | 14 | return new[] 15 | { 16 | new BeatmapStatistic 17 | { 18 | Name = @"Cherry Count", 19 | Content = totalCount.ToString(), 20 | CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles) 21 | } 22 | }; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Beatmaps/BosuBeatmapConverter.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using osu.Game.Rulesets.Objects.Types; 5 | using osu.Game.Rulesets.Objects; 6 | using System.Threading; 7 | using osu.Game.Rulesets.Bosu.Objects; 8 | using osu.Game.Rulesets.Bosu.Extensions; 9 | using osuTK; 10 | using osu.Framework.Utils; 11 | using System; 12 | 13 | namespace osu.Game.Rulesets.Bosu.Beatmaps 14 | { 15 | public class BosuBeatmapConverter : BeatmapConverter 16 | { 17 | public BosuBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) 18 | : base(beatmap, ruleset) 19 | { 20 | } 21 | 22 | public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasPosition); 23 | 24 | private int index = -1; 25 | 26 | protected override IEnumerable ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken) 27 | { 28 | List hitObjects = new List(); 29 | 30 | var originalPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero; 31 | var comboData = obj as IHasCombo; 32 | 33 | bool newCombo = comboData?.NewCombo ?? false; 34 | 35 | if (newCombo) 36 | index = 0; 37 | else 38 | index++; 39 | 40 | switch (obj) 41 | { 42 | case IHasPathWithRepeats curve: 43 | 44 | double spanDuration = curve.Duration / (curve.RepeatCount + 1); 45 | bool isBuzz = spanDuration < 75 && curve.RepeatCount > 0; 46 | 47 | hitObjects.AddRange(ConversionExtensions.GenerateSliderBody(obj.StartTime, curve, originalPosition)); 48 | 49 | if (isBuzz) 50 | hitObjects.AddRange(ConversionExtensions.ConvertBuzzSlider(obj, originalPosition, beatmap, curve, spanDuration)); 51 | else 52 | hitObjects.AddRange(ConversionExtensions.ConvertDefaultSlider(obj, originalPosition, beatmap, curve, spanDuration)); 53 | 54 | break; 55 | 56 | case IHasDuration endTime: 57 | hitObjects.AddRange(ConversionExtensions.ConvertSpinner(obj.StartTime, endTime, beatmap.ControlPointInfo.TimingPointAt(obj.StartTime).BeatLength)); 58 | break; 59 | 60 | default: 61 | 62 | if (newCombo) 63 | hitObjects.AddRange(ConversionExtensions.ConvertImpactCircle(obj.StartTime, originalPosition)); 64 | else 65 | hitObjects.AddRange(ConversionExtensions.ConvertDefaultCircle(obj.StartTime, originalPosition, index)); 66 | 67 | break; 68 | } 69 | 70 | bool first = true; 71 | 72 | foreach (var h in hitObjects) 73 | { 74 | if (h is Cherry c) 75 | { 76 | c.NewCombo = first && newCombo; 77 | c.ComboOffset = comboData?.ComboOffset ?? 0; 78 | } 79 | 80 | if (h is AngledCherry m) 81 | { 82 | var bpm = beatmap.ControlPointInfo.TimingPointAt(h.StartTime).BPM; 83 | m.SpeedMultiplier *= Interpolation.ValueAt(Math.Clamp(bpm, 0, 300), 0.625f, 1.5f, 0, 300); 84 | } 85 | 86 | if (first) 87 | first = false; 88 | } 89 | 90 | return hitObjects; 91 | } 92 | 93 | protected override Beatmap CreateBeatmap() => new BosuBeatmap(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/BosuInputManager.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Input.Bindings; 2 | using osu.Game.Rulesets.UI; 3 | using System.ComponentModel; 4 | 5 | namespace osu.Game.Rulesets.Bosu 6 | { 7 | public partial class BosuInputManager : RulesetInputManager 8 | { 9 | public BosuInputManager(RulesetInfo ruleset) 10 | : base(ruleset, 0, SimultaneousBindingMode.Unique) 11 | { 12 | } 13 | } 14 | 15 | public enum BosuAction 16 | { 17 | [Description("Move Left")] 18 | MoveLeft, 19 | 20 | [Description("Move Right")] 21 | MoveRight, 22 | 23 | [Description("Jump")] 24 | Jump, 25 | 26 | [Description("Shoot")] 27 | Shoot 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/BosuRuleset.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Sprites; 4 | using osu.Game.Beatmaps; 5 | using osu.Game.Rulesets.Difficulty; 6 | using osu.Game.Rulesets.Mods; 7 | using osu.Game.Rulesets.Scoring; 8 | using osu.Game.Rulesets.UI; 9 | using System; 10 | using osu.Framework.Graphics.Textures; 11 | using osu.Framework.Input.Bindings; 12 | using osu.Game.Rulesets.Bosu.Scoring; 13 | using osu.Game.Rulesets.Bosu.Difficulty; 14 | using osu.Game.Rulesets.Bosu.Beatmaps; 15 | using osu.Game.Rulesets.Bosu.UI; 16 | using osu.Game.Rulesets.Bosu.Mods; 17 | using osu.Game.Rulesets.Bosu.Replays; 18 | using osu.Game.Rulesets.Replays.Types; 19 | using osu.Game.Rulesets.Edit; 20 | using osu.Game.Rulesets.Bosu.Edit; 21 | using osu.Game.Rulesets.Configuration; 22 | using osu.Game.Configuration; 23 | using osu.Game.Overlays.Settings; 24 | using osu.Game.Rulesets.Bosu.Configuration; 25 | using osu.Framework.Allocation; 26 | using osu.Framework.Platform; 27 | 28 | namespace osu.Game.Rulesets.Bosu 29 | { 30 | public partial class BosuRuleset : Ruleset 31 | { 32 | private DrawableBosuRuleset drawableRuleset; 33 | 34 | public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => drawableRuleset = new DrawableBosuRuleset(this, beatmap, mods); 35 | 36 | public override HealthProcessor CreateHealthProcessor(double drainStartTime) 37 | { 38 | var hp = new BosuHealthProcessor(); 39 | drawableRuleset.HealthProcessor = hp; 40 | return hp; 41 | } 42 | 43 | public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new BosuBeatmapConverter(beatmap, this); 44 | 45 | public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new BeatmapProcessor(beatmap); 46 | 47 | public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new BosuReplayFrame(); 48 | 49 | public override HitObjectComposer CreateHitObjectComposer() => new BosuHitObjectComposer(this); 50 | 51 | public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new BosuRulesetConfigManager(settings, RulesetInfo); 52 | 53 | public override RulesetSettingsSubsection CreateSettings() => new BosuSettingsSubsection(this); 54 | 55 | public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] 56 | { 57 | new KeyBinding(InputKey.Left, BosuAction.MoveLeft), 58 | new KeyBinding(InputKey.Right, BosuAction.MoveRight), 59 | new KeyBinding(InputKey.Shift, BosuAction.Jump), 60 | new KeyBinding(InputKey.Z, BosuAction.Shoot), 61 | }; 62 | 63 | public override IEnumerable GetModsFor(ModType type) 64 | { 65 | switch (type) 66 | { 67 | case ModType.DifficultyReduction: 68 | return new Mod[] 69 | { 70 | new BosuModEasy(), 71 | new BosuModNoFail(), 72 | new MultiMod(new BosuModHalfTime(), new BosuModDaycore()) 73 | }; 74 | 75 | case ModType.DifficultyIncrease: 76 | return new Mod[] 77 | { 78 | new BosuModSuddenDeath(), 79 | new BosuModHidden(), 80 | new MultiMod(new BosuModDoubleTime(), new BosuModNightcore()) 81 | }; 82 | 83 | case ModType.Automation: 84 | return new Mod[] 85 | { 86 | new BosuModAutoplay() 87 | }; 88 | 89 | case ModType.Fun: 90 | return new Mod[] 91 | { 92 | new MultiMod(new ModWindUp(), new ModWindDown()), 93 | new BosuModBarrelRoll(), 94 | new BosuModZoomIn() 95 | }; 96 | 97 | default: 98 | return Array.Empty(); 99 | } 100 | } 101 | 102 | public override string Description => "bosu!"; 103 | 104 | public override string ShortName => "bosu!"; 105 | 106 | public override string PlayingVerb => "Avoiding cherries"; 107 | 108 | public override Drawable CreateIcon() => new BosuIcon(this); 109 | 110 | protected override IEnumerable GetValidHitResults() => new[] 111 | { 112 | HitResult.Perfect 113 | }; 114 | 115 | public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new BosuDifficultyCalculator(RulesetInfo, beatmap); 116 | 117 | private partial class BosuIcon : Sprite 118 | { 119 | private readonly BosuRuleset ruleset; 120 | 121 | public BosuIcon(BosuRuleset ruleset) 122 | { 123 | this.ruleset = ruleset; 124 | } 125 | 126 | [BackgroundDependencyLoader] 127 | private void load(GameHost host) 128 | { 129 | Texture = new TextureStore(host.Renderer, new TextureLoaderStore(ruleset.CreateResourceStore()), false).Get("Textures/logo"); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Configuration/BosuRulesetConfigManager.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Configuration; 2 | using osu.Game.Rulesets.Configuration; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Configuration 5 | { 6 | public class BosuRulesetConfigManager : RulesetConfigManager 7 | { 8 | public BosuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) 9 | : base(settings, ruleset, variant) 10 | { 11 | } 12 | 13 | protected override void InitialiseDefaults() 14 | { 15 | base.InitialiseDefaults(); 16 | SetValue(BosuRulesetSetting.TransparentBackground, false); 17 | } 18 | } 19 | 20 | public enum BosuRulesetSetting 21 | { 22 | TransparentBackground 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Difficulty/BosuDifficultyCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using osu.Game.Beatmaps; 5 | using osu.Game.Rulesets.Bosu.Objects; 6 | using osu.Game.Rulesets.Difficulty; 7 | using osu.Game.Rulesets.Difficulty.Preprocessing; 8 | using osu.Game.Rulesets.Difficulty.Skills; 9 | using osu.Game.Rulesets.Mods; 10 | 11 | namespace osu.Game.Rulesets.Bosu.Difficulty 12 | { 13 | public class BosuDifficultyCalculator : DifficultyCalculator 14 | { 15 | public BosuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) 16 | : base(ruleset, beatmap) 17 | { 18 | } 19 | 20 | protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) 21 | { 22 | var objectCount = beatmap.HitObjects.Count(h => h is Cherry); 23 | var calculatedLength = beatmap.BeatmapInfo.Length / 1000 / clockRate; 24 | 25 | var sr = 0.0; 26 | if (objectCount != 0 && calculatedLength != 0.0) 27 | sr = objectCount / 15.0 / calculatedLength; 28 | 29 | return new DifficultyAttributes 30 | { 31 | StarRating = sr, 32 | Mods = mods, 33 | MaxCombo = beatmap.HitObjects.Count(h => h is Cherry) 34 | }; 35 | } 36 | 37 | protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) 38 | => Array.Empty(); 39 | 40 | protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/Blueprints/AngledCherrySelectionBlueprint.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics; 2 | using osu.Game.Rulesets.Bosu.Edit.UI; 3 | using osu.Game.Rulesets.Bosu.Objects; 4 | using osu.Game.Rulesets.Edit; 5 | 6 | namespace osu.Game.Rulesets.Bosu.Edit.Blueprints 7 | { 8 | public partial class AngledCherrySelectionBlueprint : HitObjectSelectionBlueprint 9 | { 10 | private readonly EditorCherry circle; 11 | 12 | public AngledCherrySelectionBlueprint(AngledCherry angled) 13 | : base(angled) 14 | { 15 | InternalChildren = new Drawable[] 16 | { 17 | new EditorCherryPath(angled.Position, angled.EndPosition), 18 | circle = new EditorCherry() 19 | }; 20 | } 21 | 22 | protected override void Update() 23 | { 24 | base.Update(); 25 | circle.Position = DrawableObject.Position; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/Blueprints/BosuSelectionHandler.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Screens.Edit.Compose.Components; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Edit.Blueprints 4 | { 5 | public partial class BosuSelectionHandler : EditorSelectionHandler 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/Blueprints/InstantCherrySelectionBlueprint.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Edit; 2 | using osu.Game.Rulesets.Bosu.Edit.UI; 3 | using osu.Game.Rulesets.Bosu.Objects; 4 | 5 | namespace osu.Game.Rulesets.Bosu.Edit.Blueprints 6 | { 7 | public partial class InstantCherrySelectionBlueprint : HitObjectSelectionBlueprint 8 | { 9 | public InstantCherrySelectionBlueprint(InstantCherry instant) 10 | : base(instant) 11 | { 12 | InternalChild = new EditorCherry 13 | { 14 | Position = instant.Position 15 | }; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/BosuBlueprintContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Framework.Input.Events; 3 | using osu.Game.Rulesets.Bosu.Edit.Blueprints; 4 | using osu.Game.Rulesets.Bosu.Objects; 5 | using osu.Game.Rulesets.Edit; 6 | using osu.Game.Rulesets.Objects; 7 | using osu.Game.Screens.Edit.Compose.Components; 8 | using osuTK; 9 | 10 | namespace osu.Game.Rulesets.Bosu.Edit 11 | { 12 | public partial class BosuBlueprintContainer : ComposeBlueprintContainer 13 | { 14 | public BosuBlueprintContainer(BosuHitObjectComposer composer) 15 | : base(composer) 16 | { 17 | } 18 | 19 | protected override SelectionHandler CreateSelectionHandler() => new BosuSelectionHandler(); 20 | 21 | protected override bool TryMoveBlueprints(DragEvent e, IList<(SelectionBlueprint blueprint, Vector2[] originalSnapPositions)> blueprints) 22 | { 23 | return true; 24 | } 25 | 26 | public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) 27 | { 28 | switch (hitObject) 29 | { 30 | case InstantCherry instant: 31 | return new InstantCherrySelectionBlueprint(instant); 32 | 33 | case AngledCherry angled: 34 | return new AngledCherrySelectionBlueprint(angled); 35 | } 36 | 37 | return base.CreateHitObjectBlueprintFor(hitObject); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/BosuHitObjectComposer.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Rulesets.Bosu.Objects; 3 | using osu.Game.Rulesets.Edit; 4 | using osu.Game.Rulesets.Edit.Tools; 5 | using osu.Game.Rulesets.Mods; 6 | using osu.Game.Rulesets.UI; 7 | using osu.Game.Screens.Edit.Compose.Components; 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace osu.Game.Rulesets.Bosu.Edit 12 | { 13 | public partial class BosuHitObjectComposer : HitObjectComposer 14 | { 15 | public BosuHitObjectComposer(Ruleset ruleset) 16 | : base(ruleset) 17 | { 18 | } 19 | 20 | protected override IReadOnlyList CompositionTools => Array.Empty(); 21 | 22 | protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) 23 | => new EditorDrawableBosuRuleset(ruleset, beatmap, mods); 24 | 25 | protected override ComposeBlueprintContainer CreateBlueprintContainer() => new BosuBlueprintContainer(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/EditorBosuPlayfield.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Bosu.UI; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Edit 4 | { 5 | public partial class EditorBosuPlayfield : BosuPlayfield 6 | { 7 | protected override bool EditMode => true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/EditorDrawableBosuRuleset.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Rulesets.Bosu.UI; 3 | using osu.Game.Rulesets.Mods; 4 | using osu.Game.Rulesets.UI; 5 | using System.Collections.Generic; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Edit 8 | { 9 | public partial class EditorDrawableBosuRuleset : DrawableBosuRuleset 10 | { 11 | public EditorDrawableBosuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) 12 | : base(ruleset, beatmap, mods) 13 | { 14 | } 15 | 16 | protected override Playfield CreatePlayfield() => new EditorBosuPlayfield(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/UI/EditorCherry.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Shapes; 2 | using osu.Framework.Graphics; 3 | using osuTK.Graphics; 4 | using osu.Game.Rulesets.Bosu.Extensions; 5 | using osuTK; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Edit.UI 8 | { 9 | public partial class EditorCherry : Circle 10 | { 11 | public EditorCherry() 12 | { 13 | Origin = Anchor.Centre; 14 | Size = new Vector2(IWannaExtensions.CHERRY_DIAMETER); 15 | BorderThickness = 4; 16 | BorderColour = Color4.Black; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Edit/UI/EditorCherryPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Shapes; 4 | using osuTK; 5 | using osuTK.Graphics; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Edit.UI 8 | { 9 | public partial class EditorCherryPath : Box 10 | { 11 | public EditorCherryPath(Vector2 startPosition, Vector2 endPosition) 12 | { 13 | Origin = Anchor.CentreLeft; 14 | Position = startPosition; 15 | Height = 1; 16 | Width = Vector2.Distance(startPosition, endPosition); 17 | Rotation = getRotation(startPosition, endPosition); 18 | Colour = Color4.Black; 19 | EdgeSmoothness = Vector2.One; 20 | } 21 | 22 | private static float getRotation(Vector2 startPosition, Vector2 endPosition) 23 | { 24 | float xDiff = endPosition.X - startPosition.X; 25 | float yDiff = endPosition.Y - startPosition.Y; 26 | return (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Extensions/CherryTransformableExtensions.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics; 2 | using osu.Framework.Graphics.Transforms; 3 | using osu.Game.Rulesets.Bosu.Objects.Drawables.Pieces; 4 | 5 | namespace osu.Game.Rulesets.Bosu.Extensions 6 | { 7 | public static class CherryTransformableExtensions 8 | { 9 | public static TransformSequence FlashTo(this T sprite, float value, double duration = 0, Easing easing = Easing.None) 10 | where T : CherryPiece 11 | => sprite.TransformTo(nameof(sprite.FlashStrength), value, duration, easing); 12 | 13 | public static TransformSequence FlashTo(this TransformSequence t, float value, double duration = 0, Easing easing = Easing.None) 14 | where T : CherryPiece 15 | => t.Append(o => o.FlashTo(value, duration, easing)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Extensions/ConversionExtensions.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Utils; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Beatmaps.ControlPoints; 4 | using osu.Game.Rulesets.Bosu.Objects; 5 | using osu.Game.Rulesets.Bosu.UI; 6 | using osu.Game.Rulesets.Objects; 7 | using osu.Game.Rulesets.Objects.Types; 8 | using osuTK; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading; 13 | 14 | namespace osu.Game.Rulesets.Bosu.Extensions 15 | { 16 | public static class ConversionExtensions 17 | { 18 | private static readonly Vector2 osu_playfield_size = new Vector2(512, 384); 19 | 20 | private const int bullets_per_hitcircle = 8; 21 | private const int hitcircle_angle_offset = 5; 22 | private const float slider_angle_per_span = 2f; 23 | private const int bullets_per_slider_reverse = 5; 24 | private const int max_visuals_per_slider_span = 200; 25 | 26 | private const int bullets_per_spinner_span = 20; 27 | private const float spinner_rotation_duration = 2000; 28 | private const float spinner_ring_distance = 20; 29 | 30 | public static IEnumerable ConvertDefaultCircle(double startTime, Vector2 originalPosition, int indexInCurrentCombo) 31 | => generateExplosion( 32 | startTime, 33 | bullets_per_hitcircle, 34 | toPlayfieldSpace(originalPosition * new Vector2(1, 0.4f)), 35 | hitcircle_angle_offset * indexInCurrentCombo); 36 | 37 | public static IEnumerable ConvertImpactCircle(double startTime, Vector2 originalPosition) 38 | => generatePolygonExplosion( 39 | startTime, 40 | 4, 41 | MathExtensions.GetRandomTimedBool(startTime) ? 3 : 4, 42 | toPlayfieldSpace(originalPosition * new Vector2(1, 0.4f)), 43 | MathExtensions.GetRandomTimedAngleOffset(startTime)); 44 | 45 | public static IEnumerable ConvertDefaultSlider(HitObject obj, Vector2 originalPosition, IBeatmap beatmap, IHasPathWithRepeats curve, double spanDuration) 46 | { 47 | List converted = new List(); 48 | 49 | var difficulty = beatmap.BeatmapInfo.Difficulty; 50 | 51 | var controlPointInfo = beatmap.ControlPointInfo; 52 | TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(obj.StartTime); 53 | 54 | double scoringDistance = 100 * difficulty.SliderMultiplier; 55 | 56 | var velocity = scoringDistance / timingPoint.BeatLength; 57 | var tickDistance = scoringDistance / difficulty.SliderTickRate; 58 | 59 | foreach (var e in SliderEventGenerator.Generate(obj.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, new CancellationToken())) 60 | { 61 | var curvePosition = curve.CurvePositionAt(e.PathProgress / (curve.RepeatCount + 1)) + originalPosition; 62 | var sliderEventPosition = toPlayfieldSpace(curvePosition * new Vector2(1, 0.4f)); 63 | 64 | if (!withinPlayfield(sliderEventPosition)) 65 | continue; 66 | 67 | switch (e.Type) 68 | { 69 | case SliderEventType.Repeat: 70 | converted.AddRange(generateExplosion(e.Time, Math.Clamp((int)curve.Distance / 15, 3, 15), sliderEventPosition, MathExtensions.GetRandomTimedAngleOffset(e.Time))); 71 | break; 72 | 73 | case SliderEventType.Tail: 74 | converted.AddRange(generateExplosion(e.Time, Math.Clamp((int)curve.Distance * (curve.RepeatCount + 1) / 15, 5, 20), sliderEventPosition, MathExtensions.GetRandomTimedAngleOffset(e.Time))); 75 | break; 76 | } 77 | } 78 | 79 | return converted; 80 | } 81 | 82 | public static IEnumerable ConvertBuzzSlider(HitObject obj, Vector2 originalPosition, IBeatmap beatmap, IHasPathWithRepeats curve, double spanDuration) 83 | { 84 | List converted = new List(); 85 | 86 | var difficulty = beatmap.BeatmapInfo.Difficulty; 87 | 88 | var controlPointInfo = beatmap.ControlPointInfo; 89 | TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(obj.StartTime); 90 | 91 | double scoringDistance = 100 * difficulty.SliderMultiplier; 92 | 93 | var velocity = scoringDistance / timingPoint.BeatLength; 94 | var tickDistance = scoringDistance / difficulty.SliderTickRate; 95 | 96 | var slider = SliderEventGenerator.Generate(obj.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, new CancellationToken()); 97 | 98 | var buzzPosition = toPlayfieldSpace(originalPosition * new Vector2(1, 0.4f)); 99 | var repeats = slider.Select(e => e.Type == SliderEventType.Repeat); 100 | 101 | var repeatCount = repeats.Count(); 102 | 103 | var repeatsPerSecond = 1000f / (curve.Duration / repeatCount); 104 | 105 | if (repeatsPerSecond > 10) 106 | repeatsPerSecond = 10; 107 | 108 | var totalRepeats = (int)(repeatsPerSecond * curve.Duration / 1000f); 109 | var repeatDuration = curve.Duration / totalRepeats; 110 | 111 | foreach (var e in slider) 112 | { 113 | switch (e.Type) 114 | { 115 | case SliderEventType.Head: 116 | converted.AddRange(generateExplosion(e.Time, bullets_per_slider_reverse, buzzPosition)); 117 | break; 118 | 119 | case SliderEventType.Tail: 120 | converted.AddRange(generateExplosion(e.Time, bullets_per_slider_reverse, buzzPosition, slider_angle_per_span * (repeatCount + 1))); 121 | break; 122 | } 123 | } 124 | 125 | for (int i = 0; i < totalRepeats; i++) 126 | converted.AddRange(generateExplosion(obj.StartTime + (i + 1) * repeatDuration, bullets_per_slider_reverse, buzzPosition, slider_angle_per_span * (i + 1))); 127 | 128 | return converted; 129 | } 130 | 131 | public static IEnumerable GenerateSliderBody(double startTime, IHasPathWithRepeats curve, Vector2 originalPosition) 132 | { 133 | List bodyCherries = new List(); 134 | 135 | var bodyCherriesCount = (int)(curve.Distance * (curve.RepeatCount + 1) / 15); 136 | 137 | Vector2 lastPosition = new Vector2(-10000, -10000); 138 | 139 | for (int i = 0; i < bodyCherriesCount; i++) 140 | { 141 | var progress = i / (float)bodyCherriesCount; 142 | Vector2 position = toPlayfieldSpace((curve.CurvePositionAt(progress) + originalPosition) * new Vector2(1, 0.4f)); 143 | 144 | if (i != 0 && i != bodyCherriesCount - 1) 145 | { 146 | if (Vector2.Distance(position, lastPosition) < 5) 147 | continue; 148 | } 149 | 150 | lastPosition = position; 151 | 152 | if (withinPlayfield(position)) 153 | { 154 | bodyCherries.Add(new InstantCherry 155 | { 156 | StartTime = startTime + curve.Duration * progress, 157 | Position = position 158 | }); 159 | } 160 | } 161 | 162 | if (!bodyCherries.Any()) 163 | { 164 | bodyCherries.Add(new InstantCherry 165 | { 166 | StartTime = startTime, 167 | Position = Vector2.Zero 168 | }); 169 | } 170 | 171 | return bodyCherries; 172 | } 173 | 174 | public static IEnumerable ConvertSpinner(double startTime, IHasDuration endTime, double beatLength) 175 | { 176 | // Fast bpm spinners are almost impossible to pass, nerf them. 177 | if (beatLength < 400) 178 | { 179 | while (beatLength < 400) 180 | beatLength *= 2f; 181 | } 182 | 183 | var spansPerSpinner = endTime.Duration / beatLength; 184 | 185 | for (int i = 0; i < spansPerSpinner; i++) 186 | { 187 | var spinnerProgress = i / spansPerSpinner; 188 | 189 | for (int j = 0; j < bullets_per_spinner_span; j++) 190 | { 191 | var rotationsPerSpinner = endTime.Duration / spinner_rotation_duration; 192 | var angle = (float)(((float)j / bullets_per_spinner_span * 360) + (spinnerProgress * rotationsPerSpinner * 360)); 193 | var originPosition = new Vector2(BosuPlayfield.BASE_SIZE.X / 2f, BosuPlayfield.BASE_SIZE.Y / 4f); 194 | 195 | var rotatedXPos = originPosition.X + (spinner_ring_distance * Math.Sin(angle * Math.PI / 180)); 196 | var rotatedYPos = originPosition.Y + (spinner_ring_distance * -Math.Cos(angle * Math.PI / 180)); 197 | 198 | yield return new AngledCherry 199 | { 200 | Angle = angle, 201 | StartTime = startTime + spinnerProgress * endTime.Duration, 202 | Position = new Vector2((float)rotatedXPos, (float)rotatedYPos), 203 | }; 204 | } 205 | } 206 | } 207 | 208 | private static IEnumerable generateExplosion(double startTime, int bulletCount, Vector2 position, float angleOffset = 0, float angleRange = 360f) 209 | { 210 | for (int i = 0; i < bulletCount; i++) 211 | { 212 | yield return new AngledCherry 213 | { 214 | Angle = MathExtensions.BulletDistribution(bulletCount, angleRange, i, angleOffset), 215 | StartTime = startTime, 216 | Position = position, 217 | }; 218 | } 219 | } 220 | 221 | private static IEnumerable generatePolygonExplosion(double startTime, int bullets_per_side, int verticesCount, Vector2 position, float angleOffset = 0) 222 | { 223 | for (int i = 0; i < verticesCount; i++) 224 | { 225 | foreach (var h in generatePolygonLine(startTime, bullets_per_side, verticesCount, position, i * (360f / verticesCount) + angleOffset)) 226 | yield return h; 227 | } 228 | } 229 | 230 | private static IEnumerable generatePolygonLine(double startTime, int bullets_per_side, int verticesCount, Vector2 position, float additionalOffset = 0) 231 | { 232 | var s = 1.0; 233 | var side = s / (2 * Math.Sin(360.0 / (2 * verticesCount) * Math.PI / 180)); 234 | var partDistance = s / bullets_per_side; 235 | var partialAngle = 180 * (verticesCount - 2) / (2 * verticesCount); 236 | 237 | for (int i = 0; i < bullets_per_side; i++) 238 | { 239 | var c = (float)partDistance * i; 240 | var length = Math.Sqrt(MathExtensions.Pow((float)side) + MathExtensions.Pow(c) - (2 * side * c * Math.Cos(partialAngle * Math.PI / 180))); 241 | var missingAngle = c == 0 ? 0 : Math.Acos((MathExtensions.Pow((float)side) + MathExtensions.Pow((float)length) - MathExtensions.Pow(c)) / (2 * side * length)) * 180 / Math.PI; 242 | var currentAngle = 180 + (90 - partialAngle) - missingAngle; 243 | 244 | yield return new AngledCherry 245 | { 246 | Angle = (float)currentAngle + additionalOffset, 247 | SpeedMultiplier = (float)(length / side * 1.2f), 248 | StartTime = startTime, 249 | Position = position 250 | }; 251 | } 252 | } 253 | 254 | private static Vector2 toPlayfieldSpace(Vector2 input) 255 | { 256 | var newX = Interpolation.ValueAt(Math.Clamp(input.X / osu_playfield_size.X, 0f, 1f), IWannaExtensions.TILE_SIZE, BosuPlayfield.BASE_SIZE.X - IWannaExtensions.TILE_SIZE, 0f, 1f); 257 | var newY = Interpolation.ValueAt(Math.Clamp(input.Y / osu_playfield_size.Y, 0f, 1f), IWannaExtensions.TILE_SIZE, BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.TILE_SIZE, 0f, 1f); 258 | 259 | return new Vector2(newX, newY); 260 | } 261 | 262 | private static bool withinPlayfield(Vector2 position) 263 | { 264 | return position.X > IWannaExtensions.TILE_SIZE && position.X < BosuPlayfield.BASE_SIZE.X - IWannaExtensions.TILE_SIZE && position.Y > IWannaExtensions.TILE_SIZE && position.Y < BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.TILE_SIZE; 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Extensions/IWannaExtensions.cs: -------------------------------------------------------------------------------- 1 | using osuTK; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Extensions 4 | { 5 | public static class IWannaExtensions 6 | { 7 | // Player size 8 | public static readonly Vector2 PLAYER_SIZE = new Vector2(11, 21); 9 | public static readonly float PLAYER_HALF_WIDTH = 5.5f; 10 | public static readonly float PLAYER_HALF_HEIGHT = 10.5f; 11 | 12 | // Player movement 13 | public static readonly double PLAYER_MAX_HORIZONTAL_SPEED = 0.15; 14 | public static readonly double PLAYER_MAX_VERTICAL_SPEED = 9; 15 | public static readonly double PLAYER_VERTICAL_STOP_SPEED_MULTIPLIER = 0.45; 16 | public static readonly double PLAYER_JUMP_SPEED = 8.5; 17 | public static readonly double PLAYER_JUMP2_SPEED = 7; 18 | public static readonly double PLAYER_GRAVITY_UP = 0.42; 19 | public static readonly double PLAYER_GRAVITY_DOWN = 0.442; 20 | 21 | public static readonly int BULLET_SIZE = 3; 22 | public static readonly int BULLET_SPEED = 16; 23 | public static readonly int TILE_SIZE = 32; 24 | public static readonly int CHERRY_DIAMETER = 21; 25 | public static readonly float CHERRY_RADIUS = 10.5f; 26 | public static readonly Vector2 CHERRY_FULL_SIZE = new Vector2(21, 24); 27 | public static readonly int CHERRY_ANIMATION_FRAME_TIME = 400; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Extensions/MathExtensions.cs: -------------------------------------------------------------------------------- 1 | using osuTK; 2 | using System; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Extensions 5 | { 6 | public static class MathExtensions 7 | { 8 | public static double Pow(double input) => input * input; 9 | 10 | public static bool Collided(float circleRadius, Vector2 circlePosition, Vector2 rectPosition, Vector2 rectSize) 11 | { 12 | var deltaX = circlePosition.X - Math.Max(rectPosition.X, Math.Min(circlePosition.X, rectPosition.X + rectSize.X)); 13 | var deltaY = circlePosition.Y - Math.Max(rectPosition.Y, Math.Min(circlePosition.Y, rectPosition.Y + rectSize.Y)); 14 | return Pow(deltaX) + Pow(deltaY) < Pow(circleRadius); 15 | } 16 | 17 | public static float BulletDistribution(int bulletsPerObject, float angleRange, int index, float angleOffset = 0) 18 | { 19 | return GetSafeAngle(getAngleBuffer(bulletsPerObject, angleRange) + index * getPerBulletAngle(bulletsPerObject, angleRange) + angleOffset); ; 20 | 21 | static float getAngleBuffer(int bulletsPerObject, float angleRange) => (360 - angleRange + getPerBulletAngle(bulletsPerObject, angleRange)) / 2f; 22 | 23 | static float getPerBulletAngle(int bulletsPerObject, float angleRange) => angleRange / bulletsPerObject; 24 | } 25 | 26 | public static float GetRandomTimedAngleOffset(double time) 27 | { 28 | var random = new Random((int)Math.Round(time * 100)); 29 | return (float)random.NextDouble() * 360f; 30 | } 31 | 32 | public static bool GetRandomTimedBool(double time) 33 | { 34 | var random = new Random((int)Math.Round(time * 100)); 35 | return random.NextDouble() > 0.5f; 36 | } 37 | 38 | public static float GetSafeAngle(float angle) 39 | { 40 | if (angle < 0) 41 | { 42 | while (angle < 0) 43 | angle += 360; 44 | 45 | return angle; 46 | } 47 | 48 | if (angle > 360) 49 | { 50 | angle %= 360f; 51 | return angle; 52 | } 53 | 54 | return angle; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Judgements/BosuJudgement.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Judgements; 2 | using osu.Game.Rulesets.Scoring; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Judgements 5 | { 6 | public class BosuJudgement : Judgement 7 | { 8 | public override HitResult MaxResult => HitResult.Perfect; 9 | 10 | protected override double HealthIncreaseFor(HitResult result) 11 | { 12 | if (result == HitResult.Perfect) 13 | return 0.0006f; 14 | 15 | return base.HealthIncreaseFor(result); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModAutoplay.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Rulesets.Bosu.Replays; 3 | using osu.Game.Rulesets.Mods; 4 | using System.Collections.Generic; 5 | 6 | namespace osu.Game.Rulesets.Bosu.Mods 7 | { 8 | public class BosuModAutoplay : ModAutoplay 9 | { 10 | public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) 11 | => new ModReplayData(new BosuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModBarrelRoll.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Bosu.Objects; 2 | using osu.Game.Rulesets.Mods; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Mods 5 | { 6 | public class BosuModBarrelRoll : ModBarrelRoll 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModDaycore.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Mods 4 | { 5 | public class BosuModDaycore : ModDaycore 6 | { 7 | public override double ScoreMultiplier => 0.3; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModDoubleTime.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Mods 4 | { 5 | public class BosuModDoubleTime : ModDoubleTime 6 | { 7 | public override double ScoreMultiplier => 1.1f; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModEasy.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Localisation; 2 | using osu.Game.Rulesets.Mods; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Mods 5 | { 6 | public class BosuModEasy : ModEasy 7 | { 8 | public override LocalisableString Description => @"Smaller cherries, more forgiving HP drain and three lives!"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModHalfTime.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Mods 4 | { 5 | public class BosuModHalfTime : ModHalfTime 6 | { 7 | public override double ScoreMultiplier => 0.3; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModHidden.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Localisation; 2 | using osu.Game.Rulesets.Bosu.Objects.Drawables; 3 | using osu.Game.Rulesets.Mods; 4 | using osu.Game.Rulesets.Objects.Drawables; 5 | 6 | namespace osu.Game.Rulesets.Bosu.Mods 7 | { 8 | public class BosuModHidden : ModHidden 9 | { 10 | public override double ScoreMultiplier => 1.06; 11 | 12 | public override LocalisableString Description => "Cherries will become invisible near you."; 13 | 14 | public override void ApplyToDrawableHitObject(DrawableHitObject dho) 15 | { 16 | base.ApplyToDrawableHitObject(dho); 17 | 18 | if (dho is DrawableAngledCherry c) 19 | c.HiddenApplied = true; 20 | } 21 | 22 | protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) 23 | { 24 | } 25 | 26 | protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) 27 | { 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModNightcore.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Bosu.Objects; 2 | using osu.Game.Rulesets.Mods; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Mods 5 | { 6 | public class BosuModNightcore : ModNightcore 7 | { 8 | public override double ScoreMultiplier => 1.1f; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModNoFail.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Mods 4 | { 5 | public class BosuModNoFail : ModNoFail 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModSuddenDeath.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Mods 4 | { 5 | public class BosuModSuddenDeath : ModSuddenDeath 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Mods/BosuModZoomIn.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Framework.Localisation; 3 | using osu.Game.Configuration; 4 | using osu.Game.Rulesets.Bosu.Objects; 5 | using osu.Game.Rulesets.Bosu.UI; 6 | using osu.Game.Rulesets.Mods; 7 | using osu.Game.Rulesets.UI; 8 | using osuTK; 9 | 10 | namespace osu.Game.Rulesets.Bosu.Mods 11 | { 12 | public class BosuModZoomIn : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset 13 | { 14 | public override string Name => "Zoom In"; 15 | 16 | public override string Acronym => "ZM"; 17 | 18 | public override LocalisableString Description => "Camera is too close."; 19 | 20 | public override double ScoreMultiplier => 1.0; 21 | 22 | [SettingSource("Zoom level")] 23 | public BindableNumber ZoomLevel { get; } = new BindableFloat(1.5f) 24 | { 25 | MinValue = 1.1f, 26 | MaxValue = 2.0f, 27 | Precision = 0.1f, 28 | }; 29 | 30 | public override string SettingDescription => $"Zoom: {ZoomLevel}"; 31 | 32 | public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) 33 | { 34 | var bp = (BosuPlayfield)((DrawableBosuRuleset)drawableRuleset).Playfield; 35 | bp.Scale = new Vector2(ZoomLevel.Value); 36 | } 37 | 38 | public void Update(Playfield playfield) 39 | { 40 | var bp = (BosuPlayfield)playfield; 41 | 42 | bp.Position = (new Vector2(BosuPlayfield.BASE_SIZE.X / 2f, BosuPlayfield.BASE_SIZE.Y / 2f) - bp.Player.PlayerPosition) * new Vector2(ZoomLevel.Value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/MusicHelpers/CurrentBeatmapProvider.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Bindables; 3 | using osu.Framework.Graphics.Containers; 4 | using osu.Game.Beatmaps; 5 | 6 | namespace osu.Game.Rulesets.Bosu.MusicHelpers 7 | { 8 | public partial class CurrentBeatmapProvider : CompositeDrawable 9 | { 10 | [Resolved] 11 | private Bindable working { get; set; } 12 | 13 | protected Bindable Beatmap = new Bindable(); 14 | 15 | protected override void LoadComplete() 16 | { 17 | base.LoadComplete(); 18 | Beatmap.BindTo(working); 19 | Beatmap.BindValueChanged(OnBeatmapChanged, true); 20 | } 21 | 22 | protected virtual void OnBeatmapChanged(ValueChangedEvent beatmap) 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/AngledCherry.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Beatmaps.ControlPoints; 4 | using osu.Game.Rulesets.Bosu.Extensions; 5 | using osu.Game.Rulesets.Bosu.Judgements; 6 | using osu.Game.Rulesets.Bosu.UI; 7 | using osu.Game.Rulesets.Judgements; 8 | using osuTK; 9 | using System; 10 | 11 | namespace osu.Game.Rulesets.Bosu.Objects 12 | { 13 | public class AngledCherry : Cherry 14 | { 15 | public readonly Bindable SpeedMultiplierBindable = new Bindable(1); 16 | 17 | public float SpeedMultiplier 18 | { 19 | get => SpeedMultiplierBindable.Value; 20 | set => SpeedMultiplierBindable.Value = value; 21 | } 22 | 23 | public readonly Bindable AngleBindable = new Bindable(); 24 | 25 | public float Angle 26 | { 27 | get => AngleBindable.Value; 28 | set => AngleBindable.Value = value; 29 | } 30 | 31 | public double Duration; 32 | public Vector2 EndPosition; 33 | 34 | protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) 35 | { 36 | base.ApplyDefaultsToSelf(controlPointInfo, difficulty); 37 | 38 | EndPosition = getFinalPosition(); 39 | Duration = Vector2.Distance(Position, EndPosition) / SpeedMultiplierBindable.Value * 2.8f; 40 | } 41 | 42 | public override Judgement CreateJudgement() => new BosuJudgement(); 43 | 44 | private Vector2 getFinalPosition() 45 | { 46 | var angle = MathExtensions.GetSafeAngle(Angle); 47 | 48 | float finalX = 0; 49 | float finalY = 0; 50 | 51 | switch (getTargetWall(angle)) 52 | { 53 | case Wall.Bottom: 54 | finalY = BosuPlayfield.BASE_SIZE.Y; 55 | finalX = getXPosition(Position, finalY, angle); 56 | break; 57 | 58 | case Wall.Top: 59 | finalY = 0; 60 | finalX = getXPosition(Position, finalY, angle); 61 | break; 62 | 63 | case Wall.Left: 64 | finalX = 0; 65 | finalY = getYPosition(Position, finalX, angle); 66 | break; 67 | 68 | case Wall.Right: 69 | finalX = BosuPlayfield.BASE_SIZE.X; 70 | finalY = getYPosition(Position, finalX, angle); 71 | break; 72 | } 73 | 74 | return new Vector2(finalX, finalY); 75 | } 76 | 77 | private Wall getTargetWall(float angle) 78 | { 79 | // Top/Right 80 | if (angle <= 90) 81 | { 82 | if (angle < getCornerAngle(new Vector2(BosuPlayfield.BASE_SIZE.X, 0))) 83 | return Wall.Top; 84 | 85 | return Wall.Right; 86 | } 87 | 88 | // Right/Bottom 89 | if (angle <= 180) 90 | { 91 | if (angle < getCornerAngle(new Vector2(BosuPlayfield.BASE_SIZE.X, BosuPlayfield.BASE_SIZE.Y))) 92 | return Wall.Right; 93 | 94 | return Wall.Bottom; 95 | } 96 | 97 | // Bottom/Left 98 | if (angle <= 270) 99 | { 100 | if (angle < getCornerAngle(new Vector2(0, BosuPlayfield.BASE_SIZE.Y))) 101 | return Wall.Bottom; 102 | 103 | return Wall.Left; 104 | } 105 | 106 | // Left/Top 107 | if (angle < getCornerAngle(Vector2.Zero)) 108 | return Wall.Left; 109 | 110 | return Wall.Top; 111 | } 112 | 113 | private float getCornerAngle(Vector2 cornerPosition) 114 | => MathExtensions.GetSafeAngle((float)(Math.Atan2(Y - cornerPosition.Y, X - cornerPosition.X) * 180 / Math.PI) - 90); 115 | 116 | private static float getYPosition(Vector2 initialPosition, float finalX, float angle) 117 | => (float)(initialPosition.Y + ((finalX - initialPosition.X) / -Math.Tan(angle * Math.PI / 180))); 118 | 119 | private static float getXPosition(Vector2 initialPosition, float finalY, float angle) 120 | => (float)(initialPosition.X + ((finalY - initialPosition.Y) * -Math.Tan(angle * Math.PI / 180))); 121 | 122 | private enum Wall 123 | { 124 | Top, 125 | Right, 126 | Left, 127 | Bottom 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/BosuHitObject.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Rulesets.Objects; 3 | using osu.Game.Rulesets.Objects.Types; 4 | using osu.Game.Rulesets.Scoring; 5 | using osuTK; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Objects 8 | { 9 | public class BosuHitObject : HitObject, IHasPosition 10 | { 11 | public readonly Bindable PositionBindable = new Bindable(); 12 | 13 | public virtual Vector2 Position 14 | { 15 | get => PositionBindable.Value; 16 | set => PositionBindable.Value = value; 17 | } 18 | 19 | protected override HitWindows CreateHitWindows() => HitWindows.Empty; 20 | 21 | public float X 22 | { 23 | get => Position.X; 24 | set => Position = new Vector2(value, Position.Y); 25 | } 26 | 27 | public float Y 28 | { 29 | get => Position.Y; 30 | set => Position = new Vector2(Position.X, value); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Cherry.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Rulesets.Objects; 3 | using osu.Game.Rulesets.Objects.Types; 4 | 5 | namespace osu.Game.Rulesets.Bosu.Objects 6 | { 7 | public abstract class Cherry : BosuHitObject, IHasComboInformation 8 | { 9 | public double TimePreempt { get; set; } = 400; 10 | 11 | public virtual bool NewCombo { get; set; } 12 | 13 | private HitObjectProperty comboOffset; 14 | 15 | public Bindable ComboOffsetBindable => comboOffset.Bindable; 16 | 17 | public int ComboOffset 18 | { 19 | get => comboOffset.Value; 20 | set => comboOffset.Value = value; 21 | } 22 | 23 | private HitObjectProperty indexInCurrentCombo; 24 | 25 | public Bindable IndexInCurrentComboBindable => indexInCurrentCombo.Bindable; 26 | 27 | public virtual int IndexInCurrentCombo 28 | { 29 | get => indexInCurrentCombo.Value; 30 | set => indexInCurrentCombo.Value = value; 31 | } 32 | 33 | private HitObjectProperty comboIndex; 34 | 35 | public Bindable ComboIndexBindable => comboIndex.Bindable; 36 | 37 | public virtual int ComboIndex 38 | { 39 | get => comboIndex.Value; 40 | set => comboIndex.Value = value; 41 | } 42 | 43 | private HitObjectProperty comboIndexWithOffsets; 44 | 45 | public Bindable ComboIndexWithOffsetsBindable => comboIndexWithOffsets.Bindable; 46 | 47 | public int ComboIndexWithOffsets 48 | { 49 | get => comboIndexWithOffsets.Value; 50 | set => comboIndexWithOffsets.Value = value; 51 | } 52 | 53 | private HitObjectProperty lastInCombo; 54 | 55 | public Bindable LastInComboBindable => lastInCombo.Bindable; 56 | 57 | public bool LastInCombo 58 | { 59 | get => lastInCombo.Value; 60 | set => lastInCombo.Value = value; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/DrawableAngledCherry.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Framework.Graphics; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables 5 | { 6 | public partial class DrawableAngledCherry : DrawableCherry 7 | { 8 | protected override bool CanHitPlayer => true; 9 | 10 | public DrawableAngledCherry() 11 | : this(null) 12 | { 13 | } 14 | 15 | public DrawableAngledCherry([CanBeNull] AngledCherry h = null) 16 | : base(h) 17 | { 18 | } 19 | 20 | protected override void UpdateInitialTransforms() 21 | { 22 | base.UpdateInitialTransforms(); 23 | 24 | using (BeginDelayedSequence(HitObject.TimePreempt)) 25 | { 26 | this.MoveTo(HitObject.EndPosition, HitObject.Duration); 27 | } 28 | } 29 | 30 | protected override void CheckForResult(bool userTriggered, double timeOffset) 31 | { 32 | base.CheckForResult(userTriggered, timeOffset); 33 | 34 | if (timeOffset < HitObject.Duration) 35 | return; 36 | 37 | ApplyMaxResult(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/DrawableBosuHitObject.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Game.Rulesets.Objects.Drawables; 3 | 4 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables 5 | { 6 | public abstract partial class DrawableBosuHitObject : DrawableHitObject 7 | where T : BosuHitObject 8 | { 9 | protected new T HitObject => (T)base.HitObject; 10 | 11 | protected DrawableBosuHitObject([CanBeNull] BosuHitObject h = null) 12 | : base(h) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/DrawableCherry.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Framework.Allocation; 3 | using osu.Framework.Bindables; 4 | using osu.Framework.Graphics; 5 | using osuTK; 6 | using System; 7 | using osu.Game.Rulesets.Bosu.Extensions; 8 | using osu.Game.Rulesets.Bosu.Objects.Drawables.Pieces; 9 | using osu.Game.Rulesets.Objects.Drawables; 10 | using osu.Framework.Utils; 11 | 12 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables 13 | { 14 | public abstract partial class DrawableCherry : DrawableBosuHitObject 15 | where T : Cherry 16 | { 17 | private const int hidden_distance = 70; 18 | private const int hidden_distance_buffer = 50; 19 | private const int flash_duration = 300; 20 | 21 | public readonly IBindable PositionBindable = new Bindable(); 22 | 23 | protected abstract bool CanHitPlayer { get; } 24 | 25 | public Func CheckHit; 26 | public Func DistanceToPlayer; 27 | 28 | public bool HiddenApplied { get; set; } 29 | 30 | private CherryPiece piece; 31 | protected double StartTime; 32 | protected double TimePreempt; 33 | 34 | protected DrawableCherry([CanBeNull] Cherry h = null) 35 | : base(h) 36 | { 37 | } 38 | 39 | [BackgroundDependencyLoader] 40 | private void load() 41 | { 42 | Origin = Anchor.Centre; 43 | Size = new Vector2(IWannaExtensions.CHERRY_DIAMETER); 44 | AddInternal(piece = new CherryPiece 45 | { 46 | Origin = Anchor.BottomCentre, 47 | Anchor = Anchor.BottomCentre 48 | }); 49 | 50 | PositionBindable.BindValueChanged(p => Position = p.NewValue); 51 | } 52 | 53 | protected override void LoadComplete() 54 | { 55 | base.LoadComplete(); 56 | 57 | AccentColour.BindValueChanged(c => piece.CherryColour = c.NewValue, true); 58 | } 59 | 60 | protected override void Update() 61 | { 62 | base.Update(); 63 | 64 | Scale = new Vector2(getScale(Clock.CurrentTime)); 65 | updateFlash(Clock.CurrentTime); 66 | 67 | if (Judged) 68 | return; 69 | 70 | if (HiddenApplied) 71 | updateHidden(); 72 | } 73 | 74 | private void updateFlash(double time) 75 | { 76 | if (time < StartTime) 77 | { 78 | piece.FlashStrength = 0; 79 | return; 80 | } 81 | 82 | double timeOffset = Math.Clamp(time, StartTime, StartTime + flash_duration) - StartTime; 83 | piece.FlashStrength = 1f - (float)(timeOffset / flash_duration); 84 | } 85 | 86 | private float getScale(double time) 87 | { 88 | if (time < StartTime) 89 | { 90 | double timeOffset = Math.Clamp(time, StartTime - TimePreempt, StartTime) - StartTime + TimePreempt; 91 | return (float)(timeOffset / TimePreempt); 92 | } 93 | 94 | return GetScaleDuringLifetime(time); 95 | } 96 | 97 | protected virtual float GetScaleDuringLifetime(double time) => 1f; 98 | 99 | private void updateHidden() 100 | { 101 | var distance = DistanceToPlayer.Invoke(Position); 102 | piece.Alpha = Interpolation.ValueAt(Math.Clamp(distance, hidden_distance, hidden_distance + hidden_distance_buffer) - hidden_distance, 0f, 1f, 0, hidden_distance_buffer); 103 | } 104 | 105 | private double missTime; 106 | 107 | protected override void CheckForResult(bool userTriggered, double timeOffset) 108 | { 109 | if (timeOffset < 0) 110 | return; 111 | 112 | if (!CanHitPlayer) 113 | return; 114 | 115 | if (CheckHit?.Invoke(Position) ?? false) 116 | { 117 | missTime = timeOffset; 118 | ApplyMinResult(); 119 | } 120 | } 121 | 122 | protected override void UpdateHitStateTransforms(ArmedState state) 123 | { 124 | base.UpdateHitStateTransforms(state); 125 | 126 | switch (state) 127 | { 128 | case ArmedState.Miss: 129 | this.Delay(missTime).FadeOut(); 130 | break; 131 | } 132 | } 133 | 134 | protected override void OnApply() 135 | { 136 | base.OnApply(); 137 | 138 | PositionBindable.BindTo(HitObject.PositionBindable); 139 | StartTime = HitObject.StartTime; 140 | TimePreempt = HitObject.TimePreempt; 141 | } 142 | 143 | protected override void OnFree() 144 | { 145 | base.OnFree(); 146 | 147 | PositionBindable.UnbindFrom(HitObject.PositionBindable); 148 | } 149 | 150 | protected override double InitialLifetimeOffset => HitObject.TimePreempt; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/DrawableInstantCherry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using osu.Framework.Graphics; 4 | using osu.Game.Rulesets.Objects.Drawables; 5 | using osu.Game.Rulesets.Scoring; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables 8 | { 9 | public partial class DrawableInstantCherry : DrawableCherry 10 | { 11 | private const int scale_duration = 150; 12 | 13 | protected override bool CanHitPlayer => false; 14 | 15 | public DrawableInstantCherry() 16 | : this(null) 17 | { 18 | } 19 | 20 | public DrawableInstantCherry([CanBeNull] InstantCherry h = null) 21 | : base(h) 22 | { 23 | } 24 | 25 | protected override void CheckForResult(bool userTriggered, double timeOffset) 26 | { 27 | base.CheckForResult(userTriggered, timeOffset); 28 | 29 | if (timeOffset < 0) 30 | return; 31 | 32 | ApplyResult(HitResult.IgnoreHit); 33 | } 34 | 35 | protected override float GetScaleDuringLifetime(double time) 36 | { 37 | var timeOffset = Math.Clamp(time, StartTime, StartTime + scale_duration) - StartTime; 38 | return 1f - (float)(timeOffset / scale_duration); 39 | } 40 | 41 | protected override void UpdateHitStateTransforms(ArmedState state) 42 | { 43 | switch (state) 44 | { 45 | case ArmedState.Hit: 46 | this.Delay(150).FadeOut(); 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/Pieces/BeatSyncedSprite.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Audio.Track; 3 | using osu.Framework.Graphics.Sprites; 4 | using osu.Game.Beatmaps; 5 | using osu.Game.Beatmaps.ControlPoints; 6 | 7 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables.Pieces 8 | { 9 | public partial class BeatSyncedSprite : Sprite 10 | { 11 | private int lastBeat; 12 | 13 | private TimingControlPoint? lastTimingPoint { get; set; } 14 | 15 | protected bool IsKiaiTime { get; private set; } 16 | 17 | /// 18 | /// The time in milliseconds until the next beat. 19 | /// 20 | public double TimeUntilNextBeat { get; private set; } 21 | 22 | /// 23 | /// The time in milliseconds since the last beat 24 | /// 25 | public double TimeSinceLastBeat { get; private set; } 26 | 27 | /// 28 | /// How many beats per beatlength to trigger. Defaults to 1. 29 | /// 30 | public int Divisor { get; set; } = 1; 31 | 32 | /// 33 | /// An optional minimum beat length. Any beat length below this will be multiplied by two until valid. 34 | /// 35 | public double MinimumBeatLength { get; set; } 36 | 37 | /// 38 | /// Whether this container is currently tracking a beat sync provider. 39 | /// 40 | protected bool IsBeatSyncedWithTrack { get; private set; } 41 | 42 | [Resolved] 43 | protected IBeatSyncProvider BeatSyncSource { get; private set; } = null!; 44 | 45 | protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) 46 | { 47 | } 48 | 49 | protected override void Update() 50 | { 51 | TimingControlPoint timingPoint; 52 | EffectControlPoint effectPoint; 53 | 54 | IsBeatSyncedWithTrack = BeatSyncSource.Clock.IsRunning; 55 | 56 | double currentTrackTime; 57 | 58 | if (IsBeatSyncedWithTrack) 59 | { 60 | currentTrackTime = BeatSyncSource.Clock.CurrentTime; 61 | 62 | timingPoint = BeatSyncSource.ControlPoints?.TimingPointAt(currentTrackTime) ?? TimingControlPoint.DEFAULT; 63 | effectPoint = BeatSyncSource.ControlPoints?.EffectPointAt(currentTrackTime) ?? EffectControlPoint.DEFAULT; 64 | } 65 | else 66 | { 67 | // this may be the case where the beat syncing clock has been paused. 68 | // we still want to show an idle animation, so use this container's time instead. 69 | currentTrackTime = Clock.CurrentTime; 70 | 71 | timingPoint = TimingControlPoint.DEFAULT; 72 | effectPoint = EffectControlPoint.DEFAULT; 73 | } 74 | 75 | double beatLength = timingPoint.BeatLength / Divisor; 76 | 77 | while (beatLength < MinimumBeatLength) 78 | beatLength *= 2; 79 | 80 | int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (timingPoint.OmitFirstBarLine ? 1 : 0); 81 | 82 | // The beats before the start of the first control point are off by 1, this should do the trick 83 | if (currentTrackTime < timingPoint.Time) 84 | beatIndex--; 85 | 86 | TimeUntilNextBeat = (timingPoint.Time - currentTrackTime) % beatLength; 87 | if (TimeUntilNextBeat <= 0) 88 | TimeUntilNextBeat += beatLength; 89 | 90 | TimeSinceLastBeat = beatLength - TimeUntilNextBeat; 91 | 92 | if (ReferenceEquals(timingPoint, lastTimingPoint) && beatIndex == lastBeat) 93 | return; 94 | 95 | OnNewBeat(beatIndex, timingPoint, effectPoint, BeatSyncSource.CurrentAmplitudes); 96 | 97 | lastBeat = beatIndex; 98 | lastTimingPoint = timingPoint; 99 | 100 | IsKiaiTime = effectPoint.KiaiMode; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/Drawables/Pieces/CherryPiece.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Audio.Track; 3 | using osu.Framework.Graphics; 4 | using osu.Framework.Graphics.Colour; 5 | using osu.Framework.Graphics.Rendering; 6 | using osu.Framework.Graphics.Sprites; 7 | using osu.Framework.Graphics.Textures; 8 | using osu.Game.Beatmaps.ControlPoints; 9 | using osu.Game.Rulesets.Bosu.Extensions; 10 | using osuTK.Graphics; 11 | 12 | namespace osu.Game.Rulesets.Bosu.Objects.Drawables.Pieces 13 | { 14 | public partial class CherryPiece : BeatSyncedSprite 15 | { 16 | private Color4 cherryColour = Color4.White; 17 | 18 | public Color4 CherryColour 19 | { 20 | get => cherryColour; 21 | set 22 | { 23 | cherryColour = value; 24 | Invalidate(Invalidation.DrawNode); 25 | } 26 | } 27 | 28 | private bool state; 29 | 30 | public bool State 31 | { 32 | get => state; 33 | set 34 | { 35 | state = value; 36 | Texture = state ? base1 : base2; 37 | } 38 | } 39 | 40 | private float flashStrength; 41 | 42 | public float FlashStrength 43 | { 44 | get => flashStrength; 45 | set 46 | { 47 | if (flashStrength == value) 48 | return; 49 | 50 | flashStrength = value; 51 | Invalidate(Invalidation.DrawNode); 52 | } 53 | } 54 | 55 | private Texture base1; 56 | private Texture base2; 57 | private Texture overlay1; 58 | private Texture overlay2; 59 | private Texture flash1; 60 | private Texture flash2; 61 | 62 | [BackgroundDependencyLoader] 63 | private void load(TextureStore textures) 64 | { 65 | Size = IWannaExtensions.CHERRY_FULL_SIZE; 66 | 67 | base1 = textures.Get("Cherry/cherry-base-1"); 68 | base2 = textures.Get("Cherry/cherry-base-2"); 69 | overlay1 = textures.Get("Cherry/cherry-overlay-1"); 70 | overlay2 = textures.Get("Cherry/cherry-overlay-2"); 71 | flash1 = textures.Get("Cherry/cherry-flash-1"); 72 | flash2 = textures.Get("Cherry/cherry-flash-2"); 73 | } 74 | 75 | protected override void LoadComplete() 76 | { 77 | base.LoadComplete(); 78 | Invalidate(Invalidation.DrawNode); 79 | } 80 | 81 | protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) 82 | { 83 | State = beatIndex % 2 == 0; 84 | } 85 | 86 | public void Flash(double duration) => this.FlashTo(1f, 10, Easing.OutQuint).Then().FlashTo(0f, duration); 87 | 88 | protected override DrawNode CreateDrawNode() => new CherryDrawNode(this); 89 | 90 | private class CherryDrawNode : SpriteDrawNode 91 | { 92 | private readonly CherryPiece source; 93 | 94 | public CherryDrawNode(CherryPiece source) 95 | : base(source) 96 | { 97 | this.source = source; 98 | } 99 | 100 | private Texture overlayTexture; 101 | private Texture flashTexture; 102 | private ColourInfo cherryColour; 103 | private ColourInfo flashColour; 104 | private float flashStrength; 105 | 106 | public override void ApplyState() 107 | { 108 | base.ApplyState(); 109 | 110 | cherryColour = DrawColourInfo.Colour; 111 | cherryColour.ApplyChild(source.cherryColour); 112 | 113 | bool state = source.state; 114 | overlayTexture = state ? source.overlay1 : source.overlay2; 115 | 116 | flashStrength = source.flashStrength; 117 | 118 | if (flashStrength == 0f) 119 | return; 120 | 121 | flashTexture = state ? source.flash1 : source.flash2; 122 | 123 | flashColour = DrawColourInfo.Colour; 124 | flashColour.ApplyChild(new Color4(1f, 1f, 1f, flashStrength)); 125 | } 126 | 127 | protected override void Blit(IRenderer renderer) 128 | { 129 | if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0) 130 | return; 131 | 132 | renderer.DrawQuad(Texture, ScreenSpaceDrawQuad, cherryColour); 133 | renderer.DrawQuad(overlayTexture, ScreenSpaceDrawQuad, DrawColourInfo.Colour); 134 | 135 | if (flashStrength == 0f) 136 | return; 137 | 138 | renderer.DrawQuad(flashTexture, ScreenSpaceDrawQuad, flashColour); 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Objects/InstantCherry.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Beatmaps.ControlPoints; 3 | using osu.Game.Rulesets.Judgements; 4 | 5 | namespace osu.Game.Rulesets.Bosu.Objects 6 | { 7 | public class InstantCherry : Cherry 8 | { 9 | public override Judgement CreateJudgement() => new IgnoreJudgement(); 10 | 11 | protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) 12 | { 13 | base.ApplyDefaultsToSelf(controlPointInfo, difficulty); 14 | TimePreempt = 800; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Replays/BosuAutoGenerator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Replays; 4 | using osu.Game.Rulesets.Bosu.Beatmaps; 5 | using osu.Game.Rulesets.Mods; 6 | using osu.Game.Rulesets.Replays; 7 | 8 | namespace osu.Game.Rulesets.Bosu.Replays 9 | { 10 | public class BosuAutoGenerator : AutoGenerator 11 | { 12 | public new BosuBeatmap Beatmap => (BosuBeatmap)base.Beatmap; 13 | 14 | public BosuAutoGenerator(IBeatmap beatmap, IReadOnlyList mods) 15 | : base(beatmap) 16 | { 17 | Replay = new Replay(); 18 | } 19 | 20 | protected Replay Replay; 21 | 22 | public override Replay Generate() 23 | { 24 | Replay.Frames.Add(new BosuReplayFrame(-100000)); 25 | return Replay; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Replays/BosuFramedReplayInputHandler.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Input.StateChanges; 2 | using osu.Framework.Utils; 3 | using osu.Game.Replays; 4 | using osu.Game.Rulesets.Replays; 5 | using osuTK; 6 | using System.Collections.Generic; 7 | 8 | namespace osu.Game.Rulesets.Bosu.Replays 9 | { 10 | public class BosuFramedReplayInputHandler : FramedReplayInputHandler 11 | { 12 | public BosuFramedReplayInputHandler(Replay replay) 13 | : base(replay) 14 | { 15 | } 16 | 17 | protected override bool IsImportant(BosuReplayFrame frame) => true; 18 | 19 | protected Vector2? Position 20 | { 21 | get 22 | { 23 | var frame = CurrentFrame; 24 | 25 | if (frame == null) 26 | return null; 27 | 28 | return NextFrame != null ? 29 | new Vector2( 30 | Interpolation.ValueAt(CurrentTime, frame.Position.X, NextFrame.Position.X, frame.Time, NextFrame.Time), 31 | Interpolation.ValueAt(CurrentTime, frame.Position.Y, NextFrame.Position.Y, frame.Time, NextFrame.Time)) 32 | : frame.Position; 33 | } 34 | } 35 | 36 | protected override void CollectReplayInputs(List inputs) 37 | { 38 | if (Position.HasValue) 39 | { 40 | inputs.Add(new BosuReplayState 41 | { 42 | PressedActions = CurrentFrame?.Actions ?? new List(), 43 | Position = Position.Value 44 | }); 45 | } 46 | } 47 | 48 | public class BosuReplayState : ReplayState 49 | { 50 | public Vector2? Position { get; set; } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Replays/BosuReplayFrame.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Replays.Legacy; 4 | using osu.Game.Rulesets.Bosu.Extensions; 5 | using osu.Game.Rulesets.Bosu.UI; 6 | using osu.Game.Rulesets.Replays; 7 | using osu.Game.Rulesets.Replays.Types; 8 | using osuTK; 9 | 10 | namespace osu.Game.Rulesets.Bosu.Replays 11 | { 12 | public class BosuReplayFrame : ReplayFrame, IConvertibleReplayFrame 13 | { 14 | public List Actions = new List(); 15 | public Vector2 Position; 16 | public bool Jumping; 17 | public bool Shooting; 18 | 19 | public BosuReplayFrame() 20 | { 21 | } 22 | 23 | public BosuReplayFrame(double time, Vector2? position = null, bool jumping = false, bool shooting = false, BosuReplayFrame lastFrame = null) 24 | : base(time) 25 | { 26 | Position = position ?? new Vector2(-100, BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.PLAYER_SIZE.Y / 2f - IWannaExtensions.TILE_SIZE); 27 | Jumping = jumping; 28 | Shooting = shooting; 29 | 30 | if (Jumping) 31 | Actions.Add(BosuAction.Jump); 32 | 33 | if (Shooting) 34 | Actions.Add(BosuAction.Shoot); 35 | 36 | if (lastFrame != null) 37 | { 38 | if (Position.X > lastFrame.Position.X) 39 | lastFrame.Actions.Add(BosuAction.MoveRight); 40 | else if (Position.X < lastFrame.Position.X) 41 | lastFrame.Actions.Add(BosuAction.MoveLeft); 42 | } 43 | } 44 | 45 | public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) 46 | { 47 | Position = currentFrame.Position; 48 | Jumping = currentFrame.ButtonState == ReplayButtonState.Left1; 49 | Shooting = currentFrame.ButtonState == ReplayButtonState.Left2; 50 | 51 | if (Jumping) 52 | Actions.Add(BosuAction.Jump); 53 | 54 | if (Shooting) 55 | Actions.Add(BosuAction.Shoot); 56 | 57 | if (lastFrame is BosuReplayFrame lastBosuFrame) 58 | { 59 | if (Position.X > lastBosuFrame.Position.X) 60 | lastBosuFrame.Actions.Add(BosuAction.MoveRight); 61 | else if (Position.X < lastBosuFrame.Position.X) 62 | lastBosuFrame.Actions.Add(BosuAction.MoveLeft); 63 | } 64 | } 65 | 66 | public LegacyReplayFrame ToLegacy(IBeatmap beatmap) 67 | { 68 | ReplayButtonState state = ReplayButtonState.None; 69 | 70 | if (Actions.Contains(BosuAction.Jump)) state |= ReplayButtonState.Left1; 71 | if (Actions.Contains(BosuAction.Shoot)) state |= ReplayButtonState.Left2; 72 | 73 | return new LegacyReplayFrame(Time, Position.X, Position.Y, state); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Replays/BosuReplayRecorder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Rulesets.Bosu.UI; 3 | using osu.Game.Rulesets.Replays; 4 | using osu.Game.Rulesets.UI; 5 | using osu.Game.Scoring; 6 | using osuTK; 7 | 8 | namespace osu.Game.Rulesets.Bosu.Replays 9 | { 10 | public partial class BosuReplayRecorder : ReplayRecorder 11 | { 12 | private readonly BosuPlayfield playfield; 13 | 14 | public BosuReplayRecorder(Score score, BosuPlayfield playfield) 15 | : base(score) 16 | { 17 | this.playfield = playfield; 18 | } 19 | 20 | protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) 21 | => new BosuReplayFrame(Time.Current, playfield.Player.PlayerPosition, actions.Contains(BosuAction.Jump), actions.Contains(BosuAction.Shoot), previousFrame as BosuReplayFrame); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Samples/death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Samples/death.wav -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Samples/double-jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Samples/double-jump.wav -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Samples/entering.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Samples/entering.wav -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Samples/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Samples/jump.wav -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Samples/shoot.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Samples/shoot.wav -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-base-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-base-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-base-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-base-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-flash-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-flash-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-flash-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-flash-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-overlay-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-overlay-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-overlay-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Cherry/cherry-overlay-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_fall_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_fall_0.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_fall_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_fall_1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_0.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_idle_3.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_jump_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_jump_0.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_jump_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_jump_1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_0.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_3.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Player/player_run_4.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Playfield/corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Playfield/corner.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/Playfield/pipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/Playfield/pipe.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/bullet.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/death-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/death-circle.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/death-particle-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/death-particle-white.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/death-particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/death-particle.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/game-over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/game-over.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Resources/Textures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/bosu/15455e15280d041b431fb5bd93786701281ae6f8/osu.Game.Rulesets.Bosu/Resources/Textures/logo.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/Scoring/BosuHealthProcessor.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Scoring; 2 | 3 | namespace osu.Game.Rulesets.Bosu.Scoring 4 | { 5 | public partial class BosuHealthProcessor : HealthProcessor 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/BosuPlayfield.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.UI; 2 | using osuTK; 3 | using osu.Framework.Graphics; 4 | using osu.Game.Rulesets.Bosu.UI.Player; 5 | using osu.Framework.Allocation; 6 | using osu.Game.Rulesets.Bosu.Objects; 7 | using osu.Game.Rulesets.Bosu.Objects.Drawables; 8 | using osu.Game.Rulesets.Objects.Drawables; 9 | using osu.Game.Rulesets.Bosu.Extensions; 10 | using osu.Game.Rulesets.Objects; 11 | using osu.Game.Rulesets.Bosu.Scoring; 12 | using osu.Game.Rulesets.Bosu.UI.Death; 13 | using osu.Game.Rulesets.Bosu.UI.Entering; 14 | using osu.Game.Rulesets.Bosu.Configuration; 15 | using osu.Framework.Bindables; 16 | using osu.Game.Rulesets.Mods; 17 | using System.Linq; 18 | using osu.Framework.Graphics.Containers; 19 | 20 | namespace osu.Game.Rulesets.Bosu.UI 21 | { 22 | public partial class BosuPlayfield : Playfield 23 | { 24 | public static readonly Vector2 BASE_SIZE = new Vector2(768, 608); 25 | 26 | protected virtual bool EditMode { get; } = false; 27 | 28 | [Resolved(canBeNull: true)] 29 | private BosuRulesetConfigManager config { get; set; } 30 | 31 | private readonly Bindable transparentBackground = new Bindable(); 32 | 33 | public readonly BosuPlayer Player; 34 | private readonly PlayfieldBackground bg; 35 | private readonly EnteringOverlay enteringOverlay; 36 | private readonly DeathOverlay deathOverlay; 37 | 38 | public BosuPlayfield() 39 | { 40 | Origin = Anchor.Centre; 41 | Anchor = Anchor.Centre; 42 | InternalChildren = new Drawable[] 43 | { 44 | new Container 45 | { 46 | RelativeSizeAxes = Axes.Both, 47 | Masking = true, 48 | Children = new Drawable[] 49 | { 50 | bg = new PlayfieldBackground(), 51 | HitObjectContainer, 52 | } 53 | }, 54 | new BosuPlayfieldBorder(), 55 | Player = new BosuPlayer(), 56 | enteringOverlay = new EnteringOverlay 57 | { 58 | Alpha = EditMode ? 0 : 1 59 | }, 60 | deathOverlay = new DeathOverlay() 61 | }; 62 | } 63 | 64 | [BackgroundDependencyLoader] 65 | private void load() 66 | { 67 | RegisterPool(300, 1500); 68 | RegisterPool(500, 1500); 69 | 70 | config?.BindWith(BosuRulesetSetting.TransparentBackground, transparentBackground); 71 | } 72 | 73 | protected override void LoadComplete() 74 | { 75 | base.LoadComplete(); 76 | 77 | if (EditMode) 78 | bg.Alpha = 0; 79 | else 80 | transparentBackground.BindValueChanged(transparent => bg.Alpha = transparent.NewValue ? 0 : 1, true); 81 | 82 | if (!EditMode) 83 | enteringOverlay.Enter(250); 84 | } 85 | 86 | public void ApplyHealthProcessor(BosuHealthProcessor p) 87 | { 88 | p.Failed += onDeath; 89 | } 90 | 91 | private bool onDeath() 92 | { 93 | if (!Mods.OfType().All(m => m.PerformFail())) 94 | return false; 95 | 96 | deathOverlay.Show(Player.PlayerPosition, Player.PlayerSpeed); 97 | Player.Die(); 98 | return true; 99 | } 100 | 101 | protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) 102 | { 103 | base.OnNewDrawableHitObject(drawableHitObject); 104 | 105 | switch (drawableHitObject) 106 | { 107 | case DrawableAngledCherry cherry: 108 | cherry.CheckHit += checkHit; 109 | cherry.DistanceToPlayer += getDistanceToPlayer; 110 | break; 111 | } 112 | } 113 | 114 | private float getDistanceToPlayer(Vector2 cherryPosition) => Vector2.Distance(cherryPosition, Player.PlayerPosition); 115 | 116 | private bool checkHit(Vector2 cherryPosition) 117 | { 118 | var playerPosition = Player.PlayerPosition; 119 | var isHit = MathExtensions.Collided( 120 | IWannaExtensions.CHERRY_RADIUS, 121 | cherryPosition, 122 | new Vector2(playerPosition.X - IWannaExtensions.PLAYER_HALF_WIDTH, playerPosition.Y - IWannaExtensions.PLAYER_HALF_HEIGHT), 123 | IWannaExtensions.PLAYER_SIZE); 124 | 125 | if (isHit) 126 | Player.OnHit(); 127 | 128 | return isHit; 129 | } 130 | 131 | protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new BosuHitObjectLifetimeEntry(hitObject); 132 | 133 | private class BosuHitObjectLifetimeEntry : HitObjectLifetimeEntry 134 | { 135 | public BosuHitObjectLifetimeEntry(HitObject hitObject) 136 | : base(hitObject) 137 | { 138 | } 139 | 140 | protected override double InitialLifetimeOffset 141 | { 142 | get 143 | { 144 | if (HitObject is Cherry cherry) 145 | return cherry.TimePreempt; 146 | 147 | return base.InitialLifetimeOffset; 148 | } 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/BosuPlayfieldAdjustmentContainer.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics; 2 | using osu.Framework.Graphics.Containers; 3 | using osu.Game.Rulesets.UI; 4 | using osuTK; 5 | 6 | namespace osu.Game.Rulesets.Bosu.UI 7 | { 8 | public partial class BosuPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer 9 | { 10 | protected override Container Content => content; 11 | private readonly Container content; 12 | 13 | private const float playfield_size_adjust = 0.88f; 14 | 15 | public BosuPlayfieldAdjustmentContainer() 16 | { 17 | Anchor = Anchor.Centre; 18 | Origin = Anchor.Centre; 19 | Size = new Vector2(playfield_size_adjust); 20 | 21 | InternalChild = new Container 22 | { 23 | Anchor = Anchor.Centre, 24 | Origin = Anchor.Centre, 25 | RelativeSizeAxes = Axes.Both, 26 | FillMode = FillMode.Fit, 27 | FillAspectRatio = 24f / 19, 28 | Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both } 29 | }; 30 | } 31 | 32 | private partial class ScalingContainer : Container 33 | { 34 | protected override void Update() 35 | { 36 | base.Update(); 37 | Scale = new Vector2(Parent.ChildSize.X / BosuPlayfield.BASE_SIZE.X); 38 | Size = Vector2.Divide(Vector2.One, Scale); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/BosuPlayfieldBorder.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Textures; 2 | using osu.Framework.Allocation; 3 | using osu.Framework.Graphics.Sprites; 4 | using osuTK; 5 | using osu.Framework.Graphics; 6 | using osu.Framework.Graphics.Containers; 7 | using osu.Game.Rulesets.Bosu.Extensions; 8 | 9 | namespace osu.Game.Rulesets.Bosu.UI 10 | { 11 | public partial class BosuPlayfieldBorder : Container 12 | { 13 | [BackgroundDependencyLoader] 14 | private void load() 15 | { 16 | Size = BosuPlayfield.BASE_SIZE; // 24x19 17 | Anchor = Anchor.Centre; 18 | Origin = Anchor.Centre; 19 | 20 | for (int i = 1; i <= 22; i++) 21 | { 22 | // top 23 | Add(new Pipe 24 | { 25 | X = i * IWannaExtensions.TILE_SIZE 26 | }); 27 | 28 | // bottom 29 | Add(new Pipe 30 | { 31 | Anchor = Anchor.BottomLeft, 32 | Origin = Anchor.BottomLeft, 33 | X = i * IWannaExtensions.TILE_SIZE 34 | }); 35 | } 36 | 37 | for (int i = 1; i <= 17; i++) 38 | { 39 | // left 40 | Add(new Pipe 41 | { 42 | Rotation = 90, 43 | X = IWannaExtensions.TILE_SIZE, 44 | Y = i * IWannaExtensions.TILE_SIZE, 45 | }); 46 | 47 | // right 48 | Add(new Pipe 49 | { 50 | Anchor = Anchor.TopRight, 51 | Origin = Anchor.TopRight, 52 | Rotation = -90, 53 | X = -IWannaExtensions.TILE_SIZE, 54 | Y = i * IWannaExtensions.TILE_SIZE, 55 | }); 56 | } 57 | 58 | Add(new Corner()); 59 | Add(new Corner() { Anchor = Anchor.TopRight, Origin = Anchor.TopRight }); 60 | Add(new Corner() { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft }); 61 | Add(new Corner() { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight }); 62 | } 63 | 64 | private partial class BorderPart : Sprite 65 | { 66 | private readonly string textureName; 67 | 68 | public BorderPart(string textureName) 69 | { 70 | this.textureName = textureName; 71 | Size = new Vector2(IWannaExtensions.TILE_SIZE); 72 | EdgeSmoothness = Vector2.One; 73 | } 74 | 75 | [BackgroundDependencyLoader] 76 | private void load(TextureStore textures) 77 | { 78 | Texture = textures.Get($"Playfield/{textureName}", WrapMode.Repeat, WrapMode.Repeat); 79 | } 80 | } 81 | 82 | private partial class Corner : BorderPart 83 | { 84 | public Corner() 85 | : base("corner") 86 | { 87 | } 88 | } 89 | 90 | private partial class Pipe : BorderPart 91 | { 92 | public Pipe() 93 | : base("pipe") 94 | { 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/BosuSettingsSubsection.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Localisation; 4 | using osu.Game.Overlays.Settings; 5 | using osu.Game.Rulesets.Bosu.Configuration; 6 | 7 | namespace osu.Game.Rulesets.Bosu.UI 8 | { 9 | public partial class BosuSettingsSubsection : RulesetSettingsSubsection 10 | { 11 | protected override LocalisableString Header => "bosu!"; 12 | 13 | public BosuSettingsSubsection(Ruleset ruleset) 14 | : base(ruleset) 15 | { 16 | } 17 | 18 | [BackgroundDependencyLoader] 19 | private void load() 20 | { 21 | var config = (BosuRulesetConfigManager)Config; 22 | 23 | Children = new Drawable[] 24 | { 25 | new SettingsCheckbox 26 | { 27 | LabelText = "Transparent background", 28 | Current = config.GetBindable(BosuRulesetSetting.TransparentBackground) 29 | } 30 | }; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Death/DeathOverlay.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using osu.Framework.Graphics.Shapes; 5 | using osuTK.Graphics; 6 | using osu.Framework.Allocation; 7 | using osu.Framework.Graphics.Sprites; 8 | using osu.Framework.Graphics.Textures; 9 | using osu.Framework.Graphics.Audio; 10 | using System; 11 | using osu.Framework.Utils; 12 | using osu.Game.Rulesets.Bosu.Extensions; 13 | using osu.Framework.Audio.Sample; 14 | 15 | namespace osu.Game.Rulesets.Bosu.UI.Death 16 | { 17 | public partial class DeathOverlay : CompositeDrawable 18 | { 19 | private Box tint; 20 | private Box blackFlash; 21 | private Sprite sprite; 22 | private LetterboxOverlay letterbox; 23 | private Container particles; 24 | private Sprite circle; 25 | private DrawableSample deathSample; 26 | 27 | [BackgroundDependencyLoader] 28 | private void load(TextureStore textures, ISampleStore samples) 29 | { 30 | RelativeSizeAxes = Axes.Both; 31 | InternalChildren = new Drawable[] 32 | { 33 | particles = new Container 34 | { 35 | RelativeSizeAxes = Axes.Both, 36 | Masking = true 37 | }, 38 | circle = new Sprite 39 | { 40 | Size = new Vector2(IWannaExtensions.TILE_SIZE * 5), 41 | Origin = Anchor.Centre, 42 | Scale = Vector2.Zero, 43 | Alpha = 0, 44 | AlwaysPresent = true, 45 | Texture = textures.Get("death-circle") 46 | }, 47 | tint = new Box 48 | { 49 | RelativeSizeAxes = Axes.Both, 50 | Colour = Color4.Red, 51 | Alpha = 0 52 | }, 53 | blackFlash = new Box 54 | { 55 | RelativeSizeAxes = Axes.Both, 56 | Colour = Color4.Black, 57 | Alpha = 0 58 | }, 59 | sprite = new Sprite 60 | { 61 | Anchor = Anchor.Centre, 62 | Origin = Anchor.Centre, 63 | RelativeSizeAxes = Axes.Both, 64 | Size = new Vector2(0.8f), 65 | FillMode = FillMode.Fit, 66 | Alpha = 0, 67 | }, 68 | letterbox = new LetterboxOverlay 69 | { 70 | Alpha = 0, 71 | }, 72 | deathSample = new DrawableSample(samples.Get("death")), 73 | }; 74 | 75 | sprite.Texture = textures.Get("game-over"); 76 | } 77 | 78 | public void Show(Vector2 playerPosition, Vector2 playerSpeed) 79 | { 80 | tint.FadeTo(0.6f, 260); 81 | blackFlash.FadeIn(0.8f).Then().FadeOut(180); 82 | sprite.Delay(200).FadeIn(600); 83 | letterbox.Delay(330).FadeIn(700); 84 | deathSample.Play(); 85 | 86 | var rand = new Random((int)DateTime.Now.Ticks); 87 | 88 | for (int i = 0; i < 50; i++) 89 | { 90 | var speedVector = new Vector2(Interpolation.ValueAt((float)rand.NextDouble(), -7f, 7f, 0, 1f) + playerSpeed.X, Interpolation.ValueAt((float)rand.NextDouble(), -5f, 5f, 0, 1f)); 91 | var particle = new DeathParticle(playerPosition, speedVector); 92 | particles.Add(particle); 93 | } 94 | 95 | circle.Position = playerPosition; 96 | circle.FadeIn().Delay(250).FadeOut(750, Easing.Out); 97 | circle.Colour = Color4.White; 98 | circle.FadeColour(Color4.Red, 1000, Easing.Out); 99 | circle.ScaleTo(1, 1000, Easing.Out); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Death/DeathParticle.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using osu.Framework.Graphics.Textures; 5 | using osu.Framework.Graphics.Sprites; 6 | using osu.Framework.Allocation; 7 | using osu.Framework.Utils; 8 | using System; 9 | using osu.Game.Rulesets.Bosu.Extensions; 10 | 11 | namespace osu.Game.Rulesets.Bosu.UI.Death 12 | { 13 | public partial class DeathParticle : CompositeDrawable 14 | { 15 | private const int duration = 1500; 16 | private const double gravity = 0.3; 17 | private const double max_vertical_speed = 9; 18 | 19 | private Vector2 speedVector; 20 | private double verticalSpeed = 0; 21 | 22 | public DeathParticle(Vector2 position, Vector2 speedVector) 23 | { 24 | this.speedVector = speedVector; 25 | 26 | Origin = Anchor.Centre; 27 | Position = position; 28 | Size = new Vector2(IWannaExtensions.TILE_SIZE); 29 | } 30 | 31 | private Sprite glow; 32 | private Sprite circle; 33 | 34 | [BackgroundDependencyLoader] 35 | private void load(TextureStore textures) 36 | { 37 | AddInternal(glow = new Sprite 38 | { 39 | RelativeSizeAxes = Axes.Both, 40 | Anchor = Anchor.Centre, 41 | Origin = Anchor.Centre, 42 | Texture = textures.Get("death-particle") 43 | }); 44 | AddInternal(circle = new Sprite 45 | { 46 | RelativeSizeAxes = Axes.Both, 47 | Anchor = Anchor.Centre, 48 | Origin = Anchor.Centre, 49 | Texture = textures.Get("death-particle-white") 50 | }); 51 | } 52 | 53 | protected override void LoadComplete() 54 | { 55 | base.LoadComplete(); 56 | 57 | glow.FadeOut(duration, Easing.Out); 58 | glow.ResizeTo(0.7f, duration, Easing.Out); 59 | circle.ResizeTo(0, RNG.Next(duration - 350, duration - 300), Easing.Out); 60 | 61 | verticalSpeed = speedVector.Y; 62 | } 63 | 64 | protected override void Update() 65 | { 66 | base.Update(); 67 | 68 | var elapsedFrameTime = Clock.ElapsedFrameTime; 69 | 70 | if (Math.Abs(verticalSpeed) > max_vertical_speed) 71 | verticalSpeed = Math.Sign(verticalSpeed) * max_vertical_speed; 72 | 73 | if (Precision.AlmostEquals(verticalSpeed, 0, 0.0001)) 74 | verticalSpeed = 0; 75 | 76 | var adjustedVerticalDistance = verticalSpeed * (elapsedFrameTime / 20); 77 | 78 | Y -= (float)adjustedVerticalDistance; 79 | X += (float)(speedVector.X * (elapsedFrameTime / 20)); 80 | 81 | verticalSpeed -= gravity * (elapsedFrameTime / 20); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Death/LetterboxOverlay.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics; 2 | using osu.Framework.Graphics.Colour; 3 | using osu.Framework.Graphics.Containers; 4 | using osu.Framework.Graphics.Shapes; 5 | using osuTK.Graphics; 6 | 7 | namespace osu.Game.Rulesets.Bosu.UI.Death 8 | { 9 | public partial class LetterboxOverlay : CompositeDrawable 10 | { 11 | private static readonly Color4 transparent_black = new Color4(0, 0, 0, 0); 12 | 13 | public LetterboxOverlay() 14 | { 15 | RelativeSizeAxes = Axes.Both; 16 | InternalChildren = new Drawable[] 17 | { 18 | new Container 19 | { 20 | Anchor = Anchor.TopLeft, 21 | Origin = Anchor.TopLeft, 22 | RelativeSizeAxes = Axes.Both, 23 | Height = 0.2f, 24 | Child = new Box 25 | { 26 | RelativeSizeAxes = Axes.Both, 27 | Colour = ColourInfo.GradientVertical(Color4.Black, transparent_black), 28 | } 29 | }, 30 | new Container 31 | { 32 | Anchor = Anchor.BottomLeft, 33 | Origin = Anchor.BottomLeft, 34 | RelativeSizeAxes = Axes.Both, 35 | Height = 0.2f, 36 | Child = new Box 37 | { 38 | RelativeSizeAxes = Axes.Both, 39 | Colour = ColourInfo.GradientVertical(transparent_black, Color4.Black), 40 | } 41 | } 42 | }; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/DrawableBosuRuleset.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Input; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Input.Handlers; 4 | using osu.Game.Replays; 5 | using osu.Game.Rulesets.Bosu.Objects; 6 | using osu.Game.Rulesets.Bosu.Replays; 7 | using osu.Game.Rulesets.Bosu.Scoring; 8 | using osu.Game.Rulesets.Mods; 9 | using osu.Game.Rulesets.Objects.Drawables; 10 | using osu.Game.Rulesets.UI; 11 | using osu.Game.Scoring; 12 | using System.Collections.Generic; 13 | 14 | namespace osu.Game.Rulesets.Bosu.UI 15 | { 16 | public partial class DrawableBosuRuleset : DrawableRuleset 17 | { 18 | public BosuHealthProcessor HealthProcessor 19 | { 20 | set 21 | { 22 | if (Playfield is BosuPlayfield p) 23 | p.ApplyHealthProcessor(value); 24 | } 25 | } 26 | 27 | public DrawableBosuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) 28 | : base(ruleset, beatmap, mods) 29 | { 30 | } 31 | 32 | protected override PassThroughInputManager CreateInputManager() => new BosuInputManager(Ruleset.RulesetInfo); 33 | 34 | protected override Playfield CreatePlayfield() => new BosuPlayfield(); 35 | 36 | public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new BosuPlayfieldAdjustmentContainer(); 37 | 38 | public override DrawableHitObject CreateDrawableRepresentation(BosuHitObject h) => null; 39 | 40 | protected override ReplayRecorder CreateReplayRecorder(Score score) => new BosuReplayRecorder(score, (BosuPlayfield)Playfield); 41 | 42 | protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new BosuFramedReplayInputHandler(replay); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Entering/BeatmapBackgroundSprite.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Containers; 4 | using osu.Framework.Graphics.Sprites; 5 | using osu.Framework.Graphics.Textures; 6 | using osu.Game.Beatmaps; 7 | 8 | namespace osu.Game.Rulesets.Bosu.UI.Entering 9 | { 10 | public partial class BeatmapBackgroundSprite : BufferedContainer 11 | { 12 | [Resolved] 13 | private TextureStore textures { get; set; } 14 | 15 | private readonly Sprite sprite; 16 | 17 | public BeatmapBackgroundSprite() 18 | : base(cachedFrameBuffer: true) 19 | { 20 | RelativeSizeAxes = Axes.Both; 21 | 22 | Child = sprite = new Sprite 23 | { 24 | Anchor = Anchor.Centre, 25 | Origin = Anchor.Centre, 26 | RelativeSizeAxes = Axes.Both, 27 | FillMode = FillMode.Fill, 28 | }; 29 | } 30 | 31 | public void SetBeatmap(WorkingBeatmap beatmap) 32 | { 33 | sprite.Texture = beatmap?.GetBackground() ?? textures.Get(@"Backgrounds/bg4"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Entering/BeatmapCard.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Bosu.MusicHelpers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using osu.Framework.Graphics.Containers; 5 | using osu.Game.Graphics; 6 | using osu.Framework.Graphics.Shapes; 7 | using osuTK.Graphics; 8 | using osu.Framework.Bindables; 9 | using osu.Game.Beatmaps; 10 | 11 | namespace osu.Game.Rulesets.Bosu.UI.Entering 12 | { 13 | public partial class BeatmapCard : CurrentBeatmapProvider 14 | { 15 | public static readonly Vector2 SIZE = new Vector2(290, 150); 16 | 17 | private readonly BeatmapBackgroundSprite sprite; 18 | private readonly TextFlowContainer text; 19 | 20 | public BeatmapCard() 21 | { 22 | Size = SIZE; 23 | InternalChildren = new Drawable[] 24 | { 25 | sprite = new BeatmapBackgroundSprite 26 | { 27 | Anchor = Anchor.Centre, 28 | Origin = Anchor.Centre, 29 | }, 30 | new Box 31 | { 32 | RelativeSizeAxes = Axes.Both, 33 | Colour = Color4.Black, 34 | Alpha = 0.3f 35 | }, 36 | new Container 37 | { 38 | RelativeSizeAxes = Axes.Both, 39 | Padding = new MarginPadding(5), 40 | Child = text = new TextFlowContainer(s => 41 | { 42 | s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold); 43 | s.Shadow = true; 44 | }) 45 | { 46 | AutoSizeAxes = Axes.Y, 47 | RelativeSizeAxes = Axes.X, 48 | Anchor = Anchor.BottomCentre, 49 | Origin = Anchor.BottomCentre, 50 | TextAnchor = Anchor.BottomCentre 51 | } 52 | } 53 | }; 54 | } 55 | 56 | protected override void OnBeatmapChanged(ValueChangedEvent beatmap) 57 | { 58 | base.OnBeatmapChanged(beatmap); 59 | 60 | sprite.SetBeatmap(beatmap.NewValue); 61 | text.Text = beatmap.NewValue.Beatmap.Metadata.Title; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Entering/EnteringOverlay.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Shapes; 4 | using osuTK.Graphics; 5 | using osu.Framework.Audio.Sample; 6 | using osu.Framework.Allocation; 7 | 8 | namespace osu.Game.Rulesets.Bosu.UI.Entering 9 | { 10 | public partial class EnteringOverlay : CompositeDrawable 11 | { 12 | private readonly Box box; 13 | private readonly BeatmapCard card; 14 | private Sample enteringSample; 15 | 16 | public EnteringOverlay() 17 | { 18 | Masking = true; 19 | RelativeSizeAxes = Axes.Both; 20 | InternalChildren = new Drawable[] 21 | { 22 | box = new Box 23 | { 24 | Anchor = Anchor.Centre, 25 | Origin = Anchor.Centre, 26 | RelativeSizeAxes = Axes.Both, 27 | Colour = Color4.Black 28 | }, 29 | card = new BeatmapCard 30 | { 31 | Anchor = Anchor.TopRight, 32 | Origin = Anchor.TopRight, 33 | Y = -BeatmapCard.SIZE.Y - 1, 34 | Alpha = 0, 35 | } 36 | }; 37 | } 38 | 39 | [BackgroundDependencyLoader] 40 | private void load(ISampleStore samples) 41 | { 42 | enteringSample = samples.Get("entering"); 43 | } 44 | 45 | public void Enter(double delay) 46 | { 47 | Scheduler.AddDelayed(() => enteringSample.Play(), delay); 48 | 49 | using (box.BeginDelayedSequence(delay)) 50 | box.ResizeHeightTo(0, 800, Easing.Out); 51 | 52 | using (card.BeginDelayedSequence(delay)) 53 | { 54 | card.FadeIn(); 55 | card.MoveToY(0, 900).Delay(2200).MoveToY(-BeatmapCard.SIZE.Y - 1, 800, Easing.Out).Then().FadeOut(100); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Player/AnimatedPlayerSprite.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Animations; 4 | using osu.Framework.Graphics.Textures; 5 | 6 | namespace osu.Game.Rulesets.Bosu.UI.Player 7 | { 8 | public partial class AnimatedPlayerSprite : TextureAnimation 9 | { 10 | private const double duration = 100; 11 | 12 | private readonly PlayerSprite sprite; 13 | 14 | public AnimatedPlayerSprite(PlayerSprite sprite) 15 | { 16 | this.sprite = sprite; 17 | 18 | Anchor = Anchor.Centre; 19 | Origin = Anchor.Centre; 20 | RelativeSizeAxes = Axes.Both; 21 | } 22 | 23 | [BackgroundDependencyLoader] 24 | private void load(TextureStore textures) 25 | { 26 | switch (sprite) 27 | { 28 | case PlayerSprite.Fall: 29 | for (int i = 0; i < 2; i++) 30 | AddFrame(textures.Get($"Player/player_fall_{i}"), duration); 31 | break; 32 | 33 | case PlayerSprite.Idle: 34 | for (int i = 0; i < 4; i++) 35 | AddFrame(textures.Get($"Player/player_idle_{i}"), duration); 36 | break; 37 | 38 | case PlayerSprite.Jump: 39 | for (int i = 0; i < 2; i++) 40 | AddFrame(textures.Get($"Player/player_jump_{i}"), duration); 41 | break; 42 | 43 | case PlayerSprite.Run: 44 | for (int i = 0; i < 5; i++) 45 | AddFrame(textures.Get($"Player/player_run_{i}"), duration / 2); 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Player/BosuPlayer.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Input.Bindings; 4 | using osuTK; 5 | using osu.Game.Rulesets.Bosu.Extensions; 6 | using osu.Framework.Allocation; 7 | using osu.Framework.Audio.Sample; 8 | using osu.Framework.Utils; 9 | using osu.Framework.Bindables; 10 | using osu.Game.Rulesets.UI; 11 | using osu.Game.Rulesets.Bosu.Replays; 12 | using osu.Framework.Input.Events; 13 | 14 | namespace osu.Game.Rulesets.Bosu.UI.Player 15 | { 16 | public partial class BosuPlayer : CompositeDrawable, IKeyBindingHandler 17 | { 18 | public readonly Bindable Sprite = new Bindable(PlayerSprite.Idle); 19 | 20 | public Vector2 PlayerPosition => movingPlayer.Position; 21 | 22 | public Vector2 PlayerSpeed => new Vector2((float)horizontalSpeed, (float)verticalSpeed); 23 | 24 | private bool rightwards = true; 25 | private bool midAir = false; 26 | private bool isDead; 27 | private int availableJumpCount = 2; 28 | private int horizontalDirection; 29 | private double verticalSpeed; 30 | private double horizontalSpeed; 31 | 32 | private Sample jump; 33 | private Sample doubleJump; 34 | private Sample shootSample; 35 | 36 | private readonly BulletsContainer bulletsContainer; 37 | private readonly Container movingPlayer; 38 | private readonly Container spriteContainer; 39 | 40 | public BosuPlayer() 41 | { 42 | RelativeSizeAxes = Axes.Both; 43 | InternalChildren = new Drawable[] 44 | { 45 | bulletsContainer = new BulletsContainer(), 46 | movingPlayer = new Container 47 | { 48 | Origin = Anchor.Centre, 49 | Size = IWannaExtensions.PLAYER_SIZE, 50 | Position = new Vector2(BosuPlayfield.BASE_SIZE.X / 2f, BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.PLAYER_SIZE.Y / 2f - IWannaExtensions.TILE_SIZE), 51 | Child = spriteContainer = new Container 52 | { 53 | Anchor = Anchor.BottomCentre, 54 | Origin = Anchor.BottomCentre, 55 | X = -1.5f, 56 | Size = new Vector2(IWannaExtensions.TILE_SIZE) 57 | } 58 | } 59 | }; 60 | } 61 | 62 | [BackgroundDependencyLoader] 63 | private void load(ISampleStore samples) 64 | { 65 | jump = samples.Get("jump"); 66 | doubleJump = samples.Get("double-jump"); 67 | shootSample = samples.Get("shoot"); 68 | 69 | Sprite.BindValueChanged(onSpriteChanged, true); 70 | } 71 | 72 | protected override void Update() 73 | { 74 | base.Update(); 75 | 76 | if (isDead) 77 | return; 78 | 79 | horizontalSpeed = 0; 80 | 81 | if (horizontalDirection != 0) 82 | { 83 | rightwards = horizontalDirection > 0; 84 | horizontalSpeed = 3 * (rightwards ? 1 : -1); 85 | 86 | updateSpriteDirection(); 87 | } 88 | 89 | var elapsedFrameTime = Clock.ElapsedFrameTime; 90 | var timeDifference = elapsedFrameTime / 21; 91 | 92 | if (midAir) 93 | { 94 | if (Precision.AlmostEquals(verticalSpeed, 0, 0.0001)) 95 | verticalSpeed = 0; 96 | 97 | verticalSpeed -= (verticalSpeed > 0 ? IWannaExtensions.PLAYER_GRAVITY_UP : IWannaExtensions.PLAYER_GRAVITY_DOWN) * timeDifference; 98 | 99 | if (verticalSpeed < -IWannaExtensions.PLAYER_MAX_VERTICAL_SPEED) 100 | verticalSpeed = -IWannaExtensions.PLAYER_MAX_VERTICAL_SPEED; 101 | } 102 | 103 | var replayState = (GetContainingInputManager().CurrentState as RulesetInputManagerInputState)?.LastReplayState as BosuFramedReplayInputHandler.BosuReplayState; 104 | 105 | if (replayState?.Position.Value != null) 106 | { 107 | movingPlayer.Position = replayState.Position.Value; 108 | 109 | // Required for accurate jump sounds 110 | if (verticalSpeed <= 0 && Precision.AlmostEquals(movingPlayer.Y + IWannaExtensions.PLAYER_HALF_HEIGHT, BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.TILE_SIZE, 0.01f)) 111 | resetJumpLogic(); 112 | } 113 | else 114 | { 115 | if (horizontalSpeed > 0) 116 | moveRight(elapsedFrameTime); 117 | 118 | if (horizontalSpeed < 0) 119 | moveLeft(elapsedFrameTime); 120 | 121 | if (midAir) 122 | moveVertical(timeDifference); 123 | 124 | if (verticalSpeed <= 0) 125 | { 126 | if (movingPlayer.Y + IWannaExtensions.PLAYER_HALF_HEIGHT > BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.TILE_SIZE) 127 | { 128 | movingPlayer.Y = BosuPlayfield.BASE_SIZE.Y - IWannaExtensions.TILE_SIZE - IWannaExtensions.PLAYER_HALF_HEIGHT; 129 | resetJumpLogic(); 130 | } 131 | } 132 | } 133 | 134 | updatePlayerSprite(); 135 | } 136 | 137 | public void Die() 138 | { 139 | isDead = true; 140 | movingPlayer.Hide(); 141 | } 142 | 143 | public void OnHit() 144 | { 145 | movingPlayer.FlashColour(Colour4.Red, 250, Easing.Out); 146 | } 147 | 148 | private void moveRight(double elapsedFrameTime) 149 | { 150 | movingPlayer.X += (float)(IWannaExtensions.PLAYER_MAX_HORIZONTAL_SPEED * elapsedFrameTime); 151 | 152 | if (movingPlayer.X + IWannaExtensions.PLAYER_HALF_WIDTH > BosuPlayfield.BASE_SIZE.X - IWannaExtensions.TILE_SIZE) 153 | movingPlayer.X = BosuPlayfield.BASE_SIZE.X - IWannaExtensions.TILE_SIZE - IWannaExtensions.PLAYER_HALF_WIDTH; 154 | } 155 | 156 | private void moveLeft(double elapsedFrameTime) 157 | { 158 | movingPlayer.X -= (float)(IWannaExtensions.PLAYER_MAX_HORIZONTAL_SPEED * elapsedFrameTime); 159 | 160 | if (movingPlayer.X - IWannaExtensions.PLAYER_HALF_WIDTH < IWannaExtensions.TILE_SIZE) 161 | movingPlayer.X = IWannaExtensions.TILE_SIZE + IWannaExtensions.PLAYER_HALF_WIDTH; 162 | } 163 | 164 | private void moveVertical(double timeDifference) 165 | { 166 | movingPlayer.Y -= (float)(verticalSpeed * timeDifference); 167 | } 168 | 169 | private void resetJumpLogic() 170 | { 171 | availableJumpCount = 2; 172 | verticalSpeed = 0; 173 | midAir = false; 174 | } 175 | 176 | private void updateSpriteDirection() 177 | { 178 | spriteContainer.Scale = new Vector2(rightwards ? 1 : -1, 1); 179 | spriteContainer.X = rightwards ? -1.5f : 1.5f; 180 | } 181 | 182 | public bool OnPressed(KeyBindingPressEvent e) 183 | { 184 | if (isDead) 185 | return false; 186 | 187 | switch (e.Action) 188 | { 189 | case BosuAction.MoveLeft: 190 | horizontalDirection--; 191 | return true; 192 | 193 | case BosuAction.MoveRight: 194 | horizontalDirection++; 195 | return true; 196 | 197 | case BosuAction.Jump: 198 | onJumpPressed(); 199 | return true; 200 | 201 | case BosuAction.Shoot: 202 | onShoot(); 203 | return true; 204 | } 205 | 206 | return false; 207 | } 208 | 209 | public void OnReleased(KeyBindingReleaseEvent e) 210 | { 211 | if (isDead) 212 | return; 213 | 214 | switch (e.Action) 215 | { 216 | case BosuAction.MoveLeft: 217 | horizontalDirection++; 218 | return; 219 | 220 | case BosuAction.MoveRight: 221 | horizontalDirection--; 222 | return; 223 | 224 | case BosuAction.Jump: 225 | onJumpReleased(); 226 | return; 227 | 228 | case BosuAction.Shoot: 229 | return; 230 | } 231 | } 232 | 233 | private void onJumpPressed() 234 | { 235 | midAir = true; 236 | 237 | if (availableJumpCount == 0) 238 | return; 239 | 240 | availableJumpCount--; 241 | 242 | switch (availableJumpCount) 243 | { 244 | case 1: 245 | jump.Play(); 246 | verticalSpeed = IWannaExtensions.PLAYER_JUMP_SPEED; 247 | break; 248 | 249 | case 0: 250 | doubleJump.Play(); 251 | verticalSpeed = IWannaExtensions.PLAYER_JUMP2_SPEED; 252 | break; 253 | } 254 | } 255 | 256 | private void onJumpReleased() 257 | { 258 | if (verticalSpeed < 0) 259 | return; 260 | 261 | verticalSpeed *= IWannaExtensions.PLAYER_VERTICAL_STOP_SPEED_MULTIPLIER; 262 | } 263 | 264 | private void onShoot() 265 | { 266 | shootSample.Play(); 267 | bulletsContainer.Add(PlayerPosition, rightwards); 268 | } 269 | 270 | private void onSpriteChanged(ValueChangedEvent s) 271 | { 272 | spriteContainer.Child = new AnimatedPlayerSprite(s.NewValue); 273 | } 274 | 275 | private void updatePlayerSprite() 276 | { 277 | if (verticalSpeed < 0) 278 | { 279 | Sprite.Value = PlayerSprite.Fall; 280 | return; 281 | } 282 | 283 | if (verticalSpeed > 0) 284 | { 285 | Sprite.Value = PlayerSprite.Jump; 286 | return; 287 | } 288 | 289 | if (horizontalSpeed != 0) 290 | { 291 | Sprite.Value = PlayerSprite.Run; 292 | return; 293 | } 294 | 295 | Sprite.Value = PlayerSprite.Idle; 296 | } 297 | } 298 | 299 | public enum PlayerSprite 300 | { 301 | Idle, 302 | Run, 303 | Jump, 304 | Fall 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/Player/BulletsContainer.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Graphics.Textures; 3 | using osu.Framework.Graphics; 4 | using System; 5 | using osuTK; 6 | using System.Collections.Generic; 7 | using osu.Game.Rulesets.Bosu.Extensions; 8 | using osu.Framework.Graphics.Sprites; 9 | using osu.Framework.Graphics.Primitives; 10 | using osu.Framework.Graphics.Rendering; 11 | 12 | namespace osu.Game.Rulesets.Bosu.UI.Player 13 | { 14 | public partial class BulletsContainer : Sprite 15 | { 16 | private readonly List bullets = new List(); 17 | 18 | private double currentTime; 19 | 20 | [BackgroundDependencyLoader] 21 | private void load(TextureStore textures) 22 | { 23 | RelativeSizeAxes = Axes.Both; 24 | Texture = textures.Get("bullet"); 25 | } 26 | 27 | public void Add(Vector2 position, bool rightwards) 28 | { 29 | foreach (var b in bullets) 30 | { 31 | if (!b.IsValid) 32 | { 33 | b.Reuse(position, rightwards, Time.Current); 34 | return; 35 | } 36 | } 37 | 38 | bullets.Add(new Bullet(position, rightwards, Time.Current)); 39 | } 40 | 41 | protected override void Update() 42 | { 43 | base.Update(); 44 | 45 | currentTime = Time.Current; 46 | 47 | foreach (var b in bullets) 48 | { 49 | if (currentTime < b.StartTime || currentTime > b.EndTime) 50 | b.IsValid = false; 51 | } 52 | 53 | Invalidate(Invalidation.DrawNode); 54 | } 55 | 56 | protected override DrawNode CreateDrawNode() => new BulletsDrawNote(this); 57 | 58 | private class BulletsDrawNote : SpriteDrawNode 59 | { 60 | protected new BulletsContainer Source => (BulletsContainer)base.Source; 61 | 62 | private readonly List bullets = new List(); 63 | private double currentTime; 64 | 65 | public BulletsDrawNote(BulletsContainer source) 66 | : base(source) 67 | { 68 | } 69 | 70 | public override void ApplyState() 71 | { 72 | base.ApplyState(); 73 | 74 | currentTime = Source.currentTime; 75 | 76 | bullets.Clear(); 77 | bullets.AddRange(Source.bullets); 78 | } 79 | 80 | protected override void Blit(IRenderer renderer) 81 | { 82 | foreach (var b in bullets) 83 | { 84 | if (!b.IsValid) 85 | continue; 86 | 87 | var position = Vector2.Lerp(b.InitialPosition, b.EndPosition, (float)((currentTime - b.StartTime) / (b.EndTime - b.StartTime))); 88 | var rect = getBulletRectangle(position, IWannaExtensions.BULLET_SIZE); 89 | var quad = getQuad(rect); 90 | 91 | renderer.DrawQuad( 92 | Texture, 93 | quad, 94 | DrawColourInfo.Colour); 95 | } 96 | } 97 | 98 | private Quad getQuad(RectangleF rect) => new Quad( 99 | Vector2Extensions.Transform(rect.TopLeft, DrawInfo.Matrix), 100 | Vector2Extensions.Transform(rect.TopRight, DrawInfo.Matrix), 101 | Vector2Extensions.Transform(rect.BottomLeft, DrawInfo.Matrix), 102 | Vector2Extensions.Transform(rect.BottomRight, DrawInfo.Matrix) 103 | ); 104 | 105 | private static RectangleF getBulletRectangle(Vector2 position, float size) => new RectangleF( 106 | position.X - size / 2, 107 | position.Y - size / 2, 108 | size, 109 | size); 110 | } 111 | 112 | private class Bullet 113 | { 114 | public Vector2 InitialPosition; 115 | public Vector2 EndPosition; 116 | public double StartTime; 117 | public double EndTime; 118 | public bool IsValid; 119 | 120 | public Bullet(Vector2 initialPosition, bool rightwards, double startTime) 121 | { 122 | Reuse(initialPosition, rightwards, startTime); 123 | } 124 | 125 | public void Reuse(Vector2 initialPosition, bool rightwards, double startTime) 126 | { 127 | InitialPosition = initialPosition; 128 | EndPosition = new Vector2(rightwards ? BosuPlayfield.BASE_SIZE.X - IWannaExtensions.TILE_SIZE : IWannaExtensions.TILE_SIZE, initialPosition.Y); 129 | StartTime = startTime; 130 | 131 | var dst = Math.Abs(initialPosition.X - EndPosition.X); 132 | var duration = IWannaExtensions.BULLET_SPEED / 20f * dst; 133 | EndTime = startTime + duration; 134 | 135 | IsValid = true; 136 | } 137 | } 138 | 139 | protected override void Dispose(bool isDisposing) 140 | { 141 | bullets.Clear(); 142 | base.Dispose(isDisposing); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/UI/PlayfieldBackground.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Framework.Graphics; 3 | using osu.Framework.Graphics.Shapes; 4 | using osu.Game.Beatmaps; 5 | using osu.Game.Rulesets.Bosu.MusicHelpers; 6 | using osuTK; 7 | using osuTK.Graphics; 8 | 9 | namespace osu.Game.Rulesets.Bosu.UI 10 | { 11 | public partial class PlayfieldBackground : CurrentBeatmapProvider 12 | { 13 | private const int blind_transform_duration = 50; 14 | private const int blind_count = 20; 15 | 16 | public override bool RemoveCompletedTransforms => false; 17 | 18 | private readonly Box[] blinds = new Box[blind_count]; 19 | 20 | public PlayfieldBackground() 21 | { 22 | RelativeSizeAxes = Axes.Both; 23 | AlwaysPresent = true; 24 | 25 | AddInternal(new Box 26 | { 27 | RelativeSizeAxes = Axes.Both, 28 | }); 29 | 30 | var width = 1f / blind_count; 31 | 32 | for (int i = 0; i < blind_count; i++) 33 | { 34 | blinds[i] = new Box 35 | { 36 | RelativeSizeAxes = Axes.Both, 37 | RelativePositionAxes = Axes.X, 38 | Origin = Anchor.TopCentre, 39 | Width = width, 40 | Scale = new Vector2(0, 1), 41 | X = i * width + width / 2f, 42 | Colour = Color4.Black, 43 | }; 44 | 45 | AddInternal(blinds[i]); 46 | } 47 | } 48 | 49 | protected override void OnBeatmapChanged(ValueChangedEvent beatmap) 50 | { 51 | base.OnBeatmapChanged(beatmap); 52 | 53 | var newBeatmap = beatmap.NewValue; 54 | 55 | foreach (var effectPoint in newBeatmap.Beatmap.ControlPointInfo.EffectPoints) 56 | { 57 | if (effectPoint.KiaiMode) 58 | { 59 | var bpmBeforeKiai = newBeatmap.Beatmap.ControlPointInfo.TimingPointAt(effectPoint.Time - 1).BeatLength; 60 | 61 | for (int i = 0; i < blind_count; i++) 62 | { 63 | using (blinds[i].BeginAbsoluteSequence(effectPoint.Time - bpmBeforeKiai + i * (blind_transform_duration / 2f))) 64 | blinds[i].ScaleTo(new Vector2(1, 1), blind_transform_duration, Easing.Out); 65 | } 66 | } 67 | else 68 | { 69 | var bpmBeforeKiaiOff = newBeatmap.Beatmap.ControlPointInfo.TimingPointAt(effectPoint.Time - 1).BeatLength; 70 | 71 | for (int i = 0; i < blind_count; i++) 72 | { 73 | using (blinds[i].BeginAbsoluteSequence(effectPoint.Time - bpmBeforeKiaiOff + i * (blind_transform_duration / 2f))) 74 | blinds[i].ScaleTo(new Vector2(0, 1), blind_transform_duration, Easing.Out); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Bosu/osu.Game.Rulesets.Bosu.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | osu.Game.Rulesets.Bosu 5 | Library 6 | AnyCPU 7 | osu.Game.Rulesets.Bosu 8 | 8 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------