├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── osu.Game.Rulesets.Touhosu.Tests ├── TestSceneOsuGame.cs ├── TestSceneTouhosuPlayer.cs ├── VisualTestRunner.cs └── osu.Game.Rulesets.Touhosu.Tests.csproj ├── osu.Game.Rulesets.Touhosu.sln ├── osu.Game.Rulesets.Touhosu.sln.DotSettings └── osu.Game.Rulesets.Touhosu ├── Beatmaps ├── TouhosuBeatmap.cs └── TouhosuBeatmapConverter.cs ├── Configuration └── TouhosuRulesetConfigManager.cs ├── Difficulty └── TouhosuDifficultyCalculator.cs ├── Extensions ├── ConversionExtensions.cs ├── MathExtensions.cs └── TouhosuExtensions.cs ├── Judgements ├── TickJudgement.cs └── TouhosuJudgement.cs ├── Mods ├── TouhosuModAutoplay.cs ├── TouhosuModDamageMultiplier.cs ├── TouhosuModDaycore.cs ├── TouhosuModDoubleTime.cs ├── TouhosuModEasy.cs ├── TouhosuModHalfTime.cs ├── TouhosuModHidden.cs ├── TouhosuModNightcore.cs ├── TouhosuModNoFail.cs ├── TouhosuModNoRegen.cs └── TouhosuModSuddenDeath.cs ├── Objects ├── AngeledProjectile.cs ├── ConstantMovingProjectile.cs ├── Drawables │ ├── DrawableAngeledProjectile.cs │ ├── DrawableConstantMovingProjectile.cs │ ├── DrawableInstantProjectile.cs │ ├── DrawableProjectile.cs │ ├── DrawableTouhosuHitObject.cs │ └── Pieces │ │ └── ProjectilePiece.cs ├── InstantProjectile.cs ├── Projectile.cs └── TouhosuHitObject.cs ├── Replays ├── TouhosuAutoGenerator.cs ├── TouhosuFramedReplayInputHandler.cs └── TouhosuReplayFrame.cs ├── Resources ├── Samples │ └── graze.mp3 └── Textures │ ├── HUD │ └── HP │ │ ├── 0.png │ │ ├── 25.png │ │ ├── 50.png │ │ ├── 75.png │ │ └── full.png │ ├── Player │ ├── Player │ │ ├── idle-1.png │ │ ├── idle-2.png │ │ ├── idle-3.png │ │ ├── idle-4.png │ │ ├── idle-5.png │ │ ├── idle-6.png │ │ ├── idle-7.png │ │ ├── idle-8.png │ │ ├── left-1.png │ │ ├── left-2.png │ │ ├── left-3.png │ │ ├── left-4.png │ │ ├── left-5.png │ │ ├── right-1.png │ │ ├── right-2.png │ │ ├── right-3.png │ │ ├── right-4.png │ │ └── right-5.png │ ├── card.png │ └── focus.png │ ├── Projectiles │ ├── Grain │ │ ├── overlay.png │ │ └── texture.png │ ├── Knife │ │ ├── overlay.png │ │ └── texture.png │ └── Sphere │ │ ├── overlay.png │ │ └── texture.png │ └── logo.png ├── Scoring ├── TouhosuHealthProcessor.cs └── TouhosuHitWindows.cs ├── TouhosuInputManager.cs ├── TouhosuRuleset.cs ├── UI ├── DrawableTouhosuRuleset.cs ├── HUD │ ├── HealthDisplay.cs │ └── PlayfieldBackground.cs ├── Objects │ ├── Card.cs │ ├── CardsController.cs │ ├── FocusAnimation.cs │ ├── PlayerAnimation.cs │ └── TouhosuPlayer.cs ├── TouhosuPlayfield.cs ├── TouhosuPlayfieldAdjustmentContainer.cs ├── TouhosuReplayRecorder.cs └── TouhosuSettingsSubsection.cs └── osu.Game.Rulesets.Touhosu.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 | patreon: evast 4 | -------------------------------------------------------------------------------- /.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 | # touhosu 2 | 3 | A custom game mode for osu!lazer project based on "Touhou" game. 4 | 5 | Support: 6 | 7 | https://www.patreon.com/evast 8 | 9 | https://boosty.to/evast 10 | 11 | Follow me: 12 | 13 | https://discord.gg/d2ZCzC42yH 14 | 15 | https://youtube.com/c/evast_osu 16 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu.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.Touhosu.Tests 6 | { 7 | public 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.Touhosu.Tests/TestSceneTouhosuPlayer.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using osu.Game.Tests.Visual; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Tests 5 | { 6 | [TestFixture] 7 | public class TestSceneTouhosuPlayer : PlayerTestScene 8 | { 9 | protected override Ruleset CreatePlayerRuleset() => new TouhosuRuleset(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu.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.Touhosu.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.Touhosu.Tests/osu.Game.Rulesets.Touhosu.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | osu.Game.Rulesets.Touhosu.Tests.VisualTestRunner 4 | 10 5 | 6 | 7 | 8 | 9 | 10 | WinExe 11 | net8.0 12 | osu.Game.Rulesets.Touhosu.Tests 13 | 14 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu.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.Touhosu", "osu.Game.Rulesets.Touhosu\osu.Game.Rulesets.Touhosu.csproj", "{5AE1F0F1-DAFA-46E7-959C-DA233B7C87E9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Touhosu.Tests", "osu.Game.Rulesets.Touhosu.Tests\osu.Game.Rulesets.Touhosu.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.Touhosu/Beatmaps/TouhosuBeatmap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Rulesets.Touhosu.Objects; 4 | 5 | namespace osu.Game.Rulesets.Touhosu.Beatmaps 6 | { 7 | public class TouhosuBeatmap : Beatmap 8 | { 9 | public override IEnumerable GetStatistics() 10 | { 11 | return new[] 12 | { 13 | new BeatmapStatistic 14 | { 15 | Name = @"Projectile Count", 16 | Content = HitObjects.Count.ToString(), 17 | CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles) 18 | }, 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Beatmaps/TouhosuBeatmapConverter.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 osu.Game.Rulesets.Touhosu.Objects; 7 | using osuTK; 8 | using System.Threading; 9 | using osu.Framework.Utils; 10 | using osu.Game.Rulesets.Touhosu.Extensions; 11 | 12 | namespace osu.Game.Rulesets.Touhosu.Beatmaps 13 | { 14 | public class TouhosuBeatmapConverter : BeatmapConverter 15 | { 16 | public TouhosuBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) 17 | : base(beatmap, ruleset) 18 | { 19 | } 20 | 21 | public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasPosition); 22 | 23 | private int index = -1; 24 | 25 | protected override IEnumerable ConvertHitObject(HitObject obj, IBeatmap beatmap, CancellationToken cancellationToken) 26 | { 27 | List hitObjects = new List(); 28 | 29 | var originalPosition = (obj as IHasPosition)?.Position ?? Vector2.Zero; 30 | var comboData = obj as IHasCombo; 31 | 32 | bool newCombo = comboData?.NewCombo ?? false; 33 | 34 | if (newCombo) 35 | index = 0; 36 | else 37 | index++; 38 | 39 | switch (obj) 40 | { 41 | case IHasPathWithRepeats curve: 42 | 43 | double spanDuration = curve.Duration / (curve.RepeatCount + 1); 44 | bool isBuzz = spanDuration < 75 && curve.RepeatCount > 0; 45 | 46 | hitObjects.AddRange(ConversionExtensions.GenerateSliderBody(obj.StartTime, curve, originalPosition)); 47 | 48 | if (isBuzz) 49 | hitObjects.AddRange(ConversionExtensions.ConvertBuzzSlider(obj, originalPosition, beatmap, curve, spanDuration)); 50 | else 51 | hitObjects.AddRange(ConversionExtensions.ConvertDefaultSlider(obj, originalPosition, beatmap, curve, spanDuration)); 52 | 53 | break; 54 | 55 | case IHasDuration endTime: 56 | hitObjects.AddRange(ConversionExtensions.ConvertSpinner(obj.StartTime, endTime, beatmap.ControlPointInfo.TimingPointAt(obj.StartTime).BeatLength)); 57 | break; 58 | 59 | default: 60 | 61 | if (newCombo) 62 | hitObjects.AddRange(ConversionExtensions.ConvertImpactCircle(obj.StartTime, originalPosition)); 63 | else 64 | hitObjects.AddRange(ConversionExtensions.ConvertDefaultCircle(obj.StartTime, originalPosition, index)); 65 | 66 | break; 67 | } 68 | 69 | bool first = true; 70 | 71 | foreach (var h in hitObjects) 72 | { 73 | if (h is Projectile c) 74 | { 75 | c.NewCombo = first && newCombo; 76 | c.ComboOffset = comboData?.ComboOffset ?? 0; 77 | } 78 | 79 | if (h is ConstantMovingProjectile m) 80 | { 81 | var bpm = beatmap.ControlPointInfo.TimingPointAt(h.StartTime).BPM; 82 | m.SpeedMultiplier *= Interpolation.ValueAt(bpm, 0.8f, 1.3f, 60, 300); 83 | } 84 | 85 | if (first) 86 | first = false; 87 | } 88 | 89 | return hitObjects; 90 | } 91 | 92 | protected override Beatmap CreateBeatmap() => new TouhosuBeatmap(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Configuration/TouhosuRulesetConfigManager.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Configuration; 2 | using osu.Game.Rulesets.Configuration; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Configuration 5 | { 6 | public class TouhosuRulesetConfigManager : RulesetConfigManager 7 | { 8 | public TouhosuRulesetConfigManager(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 | SetDefault(TouhosuRulesetSetting.PlayfieldDim, 0.5, 0, 1); 17 | } 18 | } 19 | 20 | public enum TouhosuRulesetSetting 21 | { 22 | PlayfieldDim 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Difficulty/TouhosuDifficultyCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using osu.Game.Beatmaps; 5 | using osu.Game.Rulesets.Difficulty; 6 | using osu.Game.Rulesets.Difficulty.Preprocessing; 7 | using osu.Game.Rulesets.Difficulty.Skills; 8 | using osu.Game.Rulesets.Mods; 9 | 10 | namespace osu.Game.Rulesets.Touhosu.Difficulty 11 | { 12 | public class TouhosuDifficultyCalculator : DifficultyCalculator 13 | { 14 | public TouhosuDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap) 15 | : base(ruleset, beatmap) 16 | { 17 | } 18 | 19 | protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) 20 | { 21 | return new DifficultyAttributes 22 | { 23 | StarRating = beatmap.HitObjects.Count / 15 / (beatmap.BeatmapInfo.Length / 1000 / clockRate) 24 | }; 25 | } 26 | 27 | protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty(); 28 | 29 | protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => Array.Empty(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Extensions/ConversionExtensions.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Beatmaps.ControlPoints; 3 | using osu.Game.Rulesets.Touhosu.Objects; 4 | using osu.Game.Rulesets.Touhosu.UI; 5 | using osu.Game.Rulesets.Objects; 6 | using osu.Game.Rulesets.Objects.Types; 7 | using osuTK; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Threading; 12 | using osu.Framework.Utils; 13 | 14 | namespace osu.Game.Rulesets.Touhosu.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 = 100; 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 bodyProjectiles = new List(); 134 | 135 | var bodyCherriesCount = Math.Min(curve.Distance * (curve.RepeatCount + 1) / 10, max_visuals_per_slider_span * (curve.RepeatCount + 1)); 136 | 137 | for (int i = 0; i < bodyCherriesCount; i++) 138 | { 139 | var progress = i / bodyCherriesCount; 140 | var position = curve.CurvePositionAt(progress) + originalPosition; 141 | position = toPlayfieldSpace(position * new Vector2(1, 0.4f)); 142 | 143 | if (!withinPlayfield(position)) 144 | continue; 145 | 146 | bodyProjectiles.Add(new InstantProjectile 147 | { 148 | StartTime = startTime + curve.Duration * progress, 149 | Position = position 150 | }); 151 | } 152 | 153 | if (!bodyProjectiles.Any()) 154 | { 155 | bodyProjectiles.Add(new InstantProjectile 156 | { 157 | StartTime = startTime, 158 | Position = new Vector2(-100) 159 | }); 160 | } 161 | 162 | return bodyProjectiles; 163 | } 164 | 165 | public static IEnumerable ConvertSpinner(double startTime, IHasDuration endTime, double beatLength) 166 | { 167 | // Fast bpm spinners are almost impossible to pass, nerf them. 168 | if (beatLength < 400) 169 | { 170 | while (beatLength < 400) 171 | beatLength *= 2f; 172 | } 173 | 174 | var spansPerSpinner = endTime.Duration / beatLength; 175 | 176 | for (int i = 0; i < spansPerSpinner; i++) 177 | { 178 | var spinnerProgress = i / spansPerSpinner; 179 | 180 | for (int j = 0; j < bullets_per_spinner_span; j++) 181 | { 182 | var rotationsPerSpinner = endTime.Duration / spinner_rotation_duration; 183 | var angle = (float)(((float)j / bullets_per_spinner_span * 360) + (spinnerProgress * rotationsPerSpinner * 360)); 184 | var originPosition = new Vector2(TouhosuPlayfield.PLAYFIELD_SIZE.X / 2f, TouhosuPlayfield.PLAYFIELD_SIZE.Y / 4f); 185 | 186 | var rotatedXPos = originPosition.X + (spinner_ring_distance * Math.Sin(angle * Math.PI / 180)); 187 | var rotatedYPos = originPosition.Y + (spinner_ring_distance * -Math.Cos(angle * Math.PI / 180)); 188 | 189 | yield return new AngeledProjectile 190 | { 191 | Angle = angle, 192 | StartTime = startTime + spinnerProgress * endTime.Duration, 193 | Position = new Vector2((float)rotatedXPos, (float)rotatedYPos), 194 | }; 195 | } 196 | } 197 | } 198 | 199 | private static IEnumerable generateExplosion(double startTime, int bulletCount, Vector2 position, float angleOffset = 0, float angleRange = 360f) 200 | { 201 | for (int i = 0; i < bulletCount; i++) 202 | { 203 | yield return new AngeledProjectile 204 | { 205 | Angle = MathExtensions.BulletDistribution(bulletCount, angleRange, i, angleOffset), 206 | StartTime = startTime, 207 | Position = position, 208 | }; 209 | } 210 | } 211 | 212 | private static IEnumerable generatePolygonExplosion(double startTime, int bullets_per_side, int verticesCount, Vector2 position, float angleOffset = 0) 213 | { 214 | for (int i = 0; i < verticesCount; i++) 215 | { 216 | foreach (var h in generatePolygonLine(startTime, bullets_per_side, verticesCount, position, i * (360f / verticesCount) + angleOffset)) 217 | yield return h; 218 | } 219 | } 220 | 221 | private static IEnumerable generatePolygonLine(double startTime, int bullets_per_side, int verticesCount, Vector2 position, float additionalOffset = 0) 222 | { 223 | var s = 1.0; 224 | var side = s / (2 * Math.Sin(360.0 / (2 * verticesCount) * Math.PI / 180)); 225 | var partDistance = s / bullets_per_side; 226 | var partialAngle = 180 * (verticesCount - 2) / (2 * verticesCount); 227 | 228 | for (int i = 0; i < bullets_per_side; i++) 229 | { 230 | var c = (float)partDistance * i; 231 | var length = Math.Sqrt(MathExtensions.Pow((float)side) + MathExtensions.Pow(c) - (2 * side * c * Math.Cos(partialAngle * Math.PI / 180))); 232 | var missingAngle = c == 0 ? 0 : Math.Acos((MathExtensions.Pow((float)side) + MathExtensions.Pow((float)length) - MathExtensions.Pow(c)) / (2 * side * length)) * 180 / Math.PI; 233 | var currentAngle = 180 + (90 - partialAngle) - missingAngle; 234 | 235 | yield return new AngeledProjectile 236 | { 237 | Angle = (float)currentAngle + additionalOffset, 238 | SpeedMultiplier = (float)(length / side * 1.2f), 239 | StartTime = startTime, 240 | Position = position 241 | }; 242 | } 243 | } 244 | 245 | private static Vector2 toPlayfieldSpace(Vector2 input) 246 | { 247 | var newX = Interpolation.ValueAt(Math.Clamp(input.X / osu_playfield_size.X, 0f, 1f), 0f, TouhosuPlayfield.PLAYFIELD_SIZE.X, 0f, 1f); 248 | var newY = Interpolation.ValueAt(Math.Clamp(input.Y / osu_playfield_size.Y, 0f, 1f), 0f, TouhosuPlayfield.PLAYFIELD_SIZE.Y, 0f, 1f); 249 | return new Vector2(newX, newY); 250 | } 251 | 252 | private static bool withinPlayfield(Vector2 position) 253 | { 254 | return position.X > 0f && position.X < TouhosuPlayfield.PLAYFIELD_SIZE.X && position.Y > 0f && position.Y < TouhosuPlayfield.PLAYFIELD_SIZE.Y; 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Extensions/MathExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace osu.Game.Rulesets.Touhosu.Extensions 4 | { 5 | public static class MathExtensions 6 | { 7 | public static double Pow(double input) => input * input; 8 | 9 | public static float BulletDistribution(int bulletsPerObject, float angleRange, int index, float angleOffset = 0) 10 | { 11 | return GetSafeAngle(getAngleBuffer(bulletsPerObject, angleRange) + index * getPerBulletAngle(bulletsPerObject, angleRange) + angleOffset); ; 12 | 13 | static float getAngleBuffer(int bulletsPerObject, float angleRange) => (360 - angleRange + getPerBulletAngle(bulletsPerObject, angleRange)) / 2f; 14 | 15 | static float getPerBulletAngle(int bulletsPerObject, float angleRange) => angleRange / bulletsPerObject; 16 | } 17 | 18 | public static float GetRandomTimedAngleOffset(double time) 19 | { 20 | var random = new Random((int)Math.Round(time * 100)); 21 | return (float)random.NextDouble() * 360f; 22 | } 23 | 24 | public static bool GetRandomTimedBool(double time) 25 | { 26 | var random = new Random((int)Math.Round(time * 100)); 27 | return random.NextDouble() > 0.5f; 28 | } 29 | 30 | public static float GetSafeAngle(float angle) 31 | { 32 | if (angle < 0) 33 | { 34 | while (angle < 0) 35 | angle += 360; 36 | 37 | return angle; 38 | } 39 | 40 | if (angle > 360) 41 | { 42 | angle %= 360f; 43 | return angle; 44 | } 45 | 46 | return angle; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Extensions/TouhosuExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace osu.Game.Rulesets.Touhosu.Extensions 2 | { 3 | public static class TouhosuExtensions 4 | { 5 | public static float SPHERE_SIZE = 20; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Judgements/TickJudgement.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Scoring; 2 | 3 | namespace osu.Game.Rulesets.Touhosu.Judgements 4 | { 5 | public class TickJudgement : TouhosuJudgement 6 | { 7 | protected override double HealthIncreaseFor(HitResult result) 8 | { 9 | switch (result) 10 | { 11 | case HitResult.Miss: 12 | return -0.05f; 13 | } 14 | 15 | return base.HealthIncreaseFor(result); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Judgements/TouhosuJudgement.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Judgements; 2 | using osu.Game.Rulesets.Scoring; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Judgements 5 | { 6 | public class TouhosuJudgement : 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.0002f; 14 | 15 | return base.HealthIncreaseFor(result); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModAutoplay.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Rulesets.Mods; 3 | using osu.Game.Rulesets.Touhosu.Replays; 4 | using System.Collections.Generic; 5 | 6 | namespace osu.Game.Rulesets.Touhosu.Mods 7 | { 8 | public class TouhosuModAutoplay : ModAutoplay 9 | { 10 | public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList mods) 11 | => new ModReplayData(new TouhosuAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "Autoplay" }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModDamageMultiplier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using osu.Framework.Bindables; 3 | using osu.Framework.Graphics.Sprites; 4 | using osu.Framework.Localisation; 5 | using osu.Game.Configuration; 6 | using osu.Game.Graphics; 7 | using osu.Game.Rulesets.Mods; 8 | using osu.Game.Rulesets.Scoring; 9 | using osu.Game.Rulesets.Touhosu.Scoring; 10 | 11 | namespace osu.Game.Rulesets.Touhosu.Mods 12 | { 13 | public class TouhosuModDamageMultiplier : Mod, IApplicableToHealthProcessor 14 | { 15 | public override double ScoreMultiplier => 1.0; 16 | 17 | public override string Name => "Damage Multiplier"; 18 | public override string Acronym => "DM"; 19 | public override IconUsage? Icon => OsuIcon.ModHardRock; 20 | public override ModType Type => ModType.DifficultyIncrease; 21 | public override LocalisableString Description => "Bullet hits hurt more!"; 22 | public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; 23 | 24 | public void ApplyToHealthProcessor(HealthProcessor healthProcessor) 25 | { 26 | ((TouhosuHealthProcessor)healthProcessor).LossMultiplier = DamageMultiplier.Value; 27 | } 28 | 29 | [SettingSource("Damage increase", "Multiplier to damage taken when hitting a projectile")] 30 | public BindableNumber DamageMultiplier { get; } = new BindableDouble 31 | { 32 | MinValue = 1.1, 33 | MaxValue = 5, 34 | Default = 3, 35 | Value = 3, 36 | Precision = 0.1 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModDaycore.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Configuration; 3 | using osu.Game.Overlays.Settings; 4 | using osu.Game.Rulesets.Mods; 5 | using osu.Game.Rulesets.Touhosu.Objects; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osu.Game.Rulesets.UI; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Mods 10 | { 11 | public class TouhosuModDaycore : ModDaycore, IApplicableToDrawableRuleset 12 | { 13 | public override double ScoreMultiplier => 0.3; 14 | 15 | [SettingSource("Preserve player speed", "Turn on to make player speed be unaffected by the song speed.", SettingControlType = typeof(SettingsCheckbox))] 16 | public BindableBool PreserveSpeed { get; } = new BindableBool(); 17 | 18 | public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) 19 | { 20 | if (!PreserveSpeed.Value) 21 | return; 22 | 23 | var playfiled = (TouhosuPlayfield)drawableRuleset.Playfield; 24 | playfiled.SpeedMultiplier = 1.0 / SpeedChange.Value; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModDoubleTime.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Configuration; 3 | using osu.Game.Overlays.Settings; 4 | using osu.Game.Rulesets.Mods; 5 | using osu.Game.Rulesets.Touhosu.Objects; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osu.Game.Rulesets.UI; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Mods 10 | { 11 | public class TouhosuModDoubleTime : ModDoubleTime, IApplicableToDrawableRuleset 12 | { 13 | public override double ScoreMultiplier => 1.1f; 14 | 15 | [SettingSource("Preserve player speed", "Turn on to make player speed be unaffected by the song speed.", SettingControlType = typeof(SettingsCheckbox))] 16 | public BindableBool PreserveSpeed { get; } = new BindableBool(); 17 | 18 | public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) 19 | { 20 | if (!PreserveSpeed.Value) 21 | return; 22 | 23 | var playfiled = (TouhosuPlayfield)drawableRuleset.Playfield; 24 | playfiled.SpeedMultiplier = 1.0 / SpeedChange.Value; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModEasy.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Localisation; 2 | using osu.Game.Rulesets.Mods; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Mods 5 | { 6 | public class TouhosuModEasy : ModEasy 7 | { 8 | public override LocalisableString Description => @"Smaller bullets, more forgiving HP drain and three lives!"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModHalfTime.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Configuration; 3 | using osu.Game.Overlays.Settings; 4 | using osu.Game.Rulesets.Mods; 5 | using osu.Game.Rulesets.Touhosu.Objects; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osu.Game.Rulesets.UI; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Mods 10 | { 11 | public class TouhosuModHalfTime : ModHalfTime, IApplicableToDrawableRuleset 12 | { 13 | public override double ScoreMultiplier => 0.3; 14 | 15 | [SettingSource("Preserve player speed", "Turn on to make player speed be unaffected by the song speed.", SettingControlType = typeof(SettingsCheckbox))] 16 | public BindableBool PreserveSpeed { get; } = new BindableBool(); 17 | 18 | public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) 19 | { 20 | if (!PreserveSpeed.Value) 21 | return; 22 | 23 | var playfiled = (TouhosuPlayfield)drawableRuleset.Playfield; 24 | playfiled.SpeedMultiplier = 1.0 / SpeedChange.Value; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModHidden.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Localisation; 2 | using osu.Game.Rulesets.Touhosu.Objects.Drawables; 3 | using osu.Game.Rulesets.Mods; 4 | using osu.Game.Rulesets.Objects.Drawables; 5 | 6 | namespace osu.Game.Rulesets.Touhosu.Mods 7 | { 8 | public class TouhosuModHidden : ModHidden 9 | { 10 | public override double ScoreMultiplier => 1.06; 11 | 12 | public override LocalisableString Description => "Bullets will become invisible near you."; 13 | 14 | public override void ApplyToDrawableHitObject(DrawableHitObject dho) 15 | { 16 | base.ApplyToDrawableHitObject(dho); 17 | 18 | if (dho is DrawableAngeledProjectile p) 19 | p.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.Touhosu/Mods/TouhosuModNightcore.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Configuration; 3 | using osu.Game.Overlays.Settings; 4 | using osu.Game.Rulesets.Touhosu.Objects; 5 | using osu.Game.Rulesets.Mods; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osu.Game.Rulesets.UI; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Mods 10 | { 11 | public class TouhosuModNightcore : ModNightcore 12 | { 13 | public override double ScoreMultiplier => 1.1f; 14 | 15 | [SettingSource("Preserve player speed", "Turn on to make player speed be unaffected by the song speed.", SettingControlType = typeof(SettingsCheckbox))] 16 | public BindableBool PreserveSpeed { get; } = new BindableBool(); 17 | 18 | public new void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) 19 | { 20 | base.ApplyToDrawableRuleset(drawableRuleset); 21 | 22 | if (!PreserveSpeed.Value) 23 | return; 24 | 25 | var playfiled = (TouhosuPlayfield)drawableRuleset.Playfield; 26 | playfiled.SpeedMultiplier = 1.0 / SpeedChange.Value; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModNoFail.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Mods; 2 | 3 | namespace osu.Game.Rulesets.Touhosu.Mods 4 | { 5 | public class TouhosuModNoFail : ModNoFail 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModNoRegen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using osu.Framework.Graphics.Sprites; 3 | using osu.Framework.Localisation; 4 | using osu.Game.Graphics; 5 | using osu.Game.Rulesets.Mods; 6 | using osu.Game.Rulesets.Scoring; 7 | using osu.Game.Rulesets.Touhosu.Scoring; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Mods 10 | { 11 | public class TouhosuModNoRegen : Mod, IApplicableToHealthProcessor 12 | { 13 | public override double ScoreMultiplier => 1.0; 14 | public override string Name => "No Regen"; 15 | public override string Acronym => "NR"; 16 | public override IconUsage? Icon => OsuIcon.HeartBreak; 17 | public override ModType Type => ModType.DifficultyIncrease; 18 | public override LocalisableString Description => "No health regeneration."; 19 | public override Type[] IncompatibleMods => new[] { typeof(ModEasy) }; 20 | 21 | public void ApplyToHealthProcessor(HealthProcessor healthProcessor) 22 | { 23 | ((TouhosuHealthProcessor)healthProcessor).NoRegen = true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Mods/TouhosuModSuddenDeath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using osu.Game.Rulesets.Mods; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Mods 5 | { 6 | public class TouhosuModSuddenDeath : ModSuddenDeath 7 | { 8 | public override Type[] IncompatibleMods => new[] { typeof(TouhosuModNoRegen), typeof(TouhosuModDamageMultiplier) }; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/AngeledProjectile.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Rulesets.Judgements; 3 | using osu.Game.Rulesets.Touhosu.Judgements; 4 | 5 | namespace osu.Game.Rulesets.Touhosu.Objects 6 | { 7 | public class AngeledProjectile : ConstantMovingProjectile 8 | { 9 | public readonly Bindable AngleBindable = new Bindable(); 10 | 11 | public float Angle 12 | { 13 | get => AngleBindable.Value; 14 | set => AngleBindable.Value = value; 15 | } 16 | 17 | public override Judgement CreateJudgement() => new TouhosuJudgement(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/ConstantMovingProjectile.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | 3 | namespace osu.Game.Rulesets.Touhosu.Objects 4 | { 5 | public abstract class ConstantMovingProjectile : Projectile 6 | { 7 | public readonly Bindable SpeedMultiplierBindable = new Bindable(1); 8 | 9 | public float SpeedMultiplier 10 | { 11 | get => SpeedMultiplierBindable.Value; 12 | set => SpeedMultiplierBindable.Value = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/DrawableAngeledProjectile.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Framework.Bindables; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables 5 | { 6 | public class DrawableAngeledProjectile : DrawableConstantMovingProjectile 7 | { 8 | public readonly IBindable AngleBindable = new Bindable(); 9 | 10 | protected override float GetTargetAngle() => AngleBindable.Value; 11 | 12 | public DrawableAngeledProjectile() 13 | : this(null) 14 | { 15 | } 16 | 17 | public DrawableAngeledProjectile([CanBeNull] AngeledProjectile h = null) 18 | : base(h) 19 | { 20 | } 21 | 22 | protected override void OnApply() 23 | { 24 | base.OnApply(); 25 | 26 | AngleBindable.BindTo(HitObject.AngleBindable); 27 | } 28 | 29 | protected override void OnFree() 30 | { 31 | base.OnFree(); 32 | 33 | AngleBindable.UnbindFrom(HitObject.AngleBindable); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/DrawableConstantMovingProjectile.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Framework.Bindables; 3 | using osu.Framework.Graphics; 4 | using osu.Game.Rulesets.Objects.Drawables; 5 | using osu.Game.Rulesets.Touhosu.Extensions; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osuTK; 8 | using System; 9 | 10 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables 11 | { 12 | public abstract partial class DrawableConstantMovingProjectile : DrawableProjectile 13 | where T : ConstantMovingProjectile 14 | { 15 | public readonly IBindable SpeedMultiplierBindable = new Bindable(); 16 | 17 | protected override bool CanHitPlayer => true; 18 | 19 | protected abstract float GetTargetAngle(); 20 | 21 | private double duration; 22 | 23 | protected DrawableConstantMovingProjectile([CanBeNull] T h = null) 24 | : base(h) 25 | { 26 | } 27 | 28 | protected override void UpdateInitialTransforms() 29 | { 30 | base.UpdateInitialTransforms(); 31 | 32 | using (BeginDelayedSequence(HitObject.TimePreempt)) 33 | { 34 | Vector2 finalPosition = getFinalPosition(); 35 | duration = Vector2.Distance(HitObject.Position, finalPosition) / SpeedMultiplierBindable.Value * 5; 36 | this.MoveTo(finalPosition, duration); 37 | } 38 | } 39 | 40 | protected override void UpdateHitStateTransforms(ArmedState state) 41 | { 42 | base.UpdateHitStateTransforms(state); 43 | 44 | switch (state) 45 | { 46 | case ArmedState.Hit: 47 | this.Delay(duration).FadeOut(); 48 | break; 49 | } 50 | } 51 | 52 | protected override void CheckForResult(bool userTriggered, double timeOffset) 53 | { 54 | base.CheckForResult(userTriggered, timeOffset); 55 | 56 | if (timeOffset < duration) 57 | return; 58 | 59 | ApplyResult((r, u) => r.Type = r.Judgement.MaxResult); 60 | } 61 | 62 | protected override void OnApply() 63 | { 64 | base.OnApply(); 65 | 66 | SpeedMultiplierBindable.BindTo(HitObject.SpeedMultiplierBindable); 67 | } 68 | 69 | protected override void OnFree() 70 | { 71 | base.OnFree(); 72 | 73 | SpeedMultiplierBindable.UnbindFrom(HitObject.SpeedMultiplierBindable); 74 | } 75 | 76 | private Vector2 getFinalPosition() 77 | { 78 | var angle = MathExtensions.GetSafeAngle(GetTargetAngle()); 79 | 80 | float finalX = 0; 81 | float finalY = 0; 82 | 83 | switch (getTargetWall(angle)) 84 | { 85 | case Wall.Bottom: 86 | finalY = TouhosuPlayfield.PLAYFIELD_SIZE.Y; 87 | finalX = getXPosition(HitObject.Position, finalY, angle); 88 | break; 89 | 90 | case Wall.Top: 91 | finalY = 0; 92 | finalX = getXPosition(HitObject.Position, finalY, angle); 93 | break; 94 | 95 | case Wall.Left: 96 | finalX = 0; 97 | finalY = getYPosition(HitObject.Position, finalX, angle); 98 | break; 99 | 100 | case Wall.Right: 101 | finalX = TouhosuPlayfield.PLAYFIELD_SIZE.X; 102 | finalY = getYPosition(HitObject.Position, finalX, angle); 103 | break; 104 | } 105 | 106 | return new Vector2(finalX, finalY); 107 | } 108 | 109 | private Wall getTargetWall(float angle) 110 | { 111 | // Top/Right 112 | if (angle <= 90) 113 | { 114 | if (angle < getCornerAngle(new Vector2(TouhosuPlayfield.PLAYFIELD_SIZE.X, 0))) 115 | return Wall.Top; 116 | 117 | return Wall.Right; 118 | } 119 | 120 | // Right/Bottom 121 | if (angle <= 180) 122 | { 123 | if (angle < getCornerAngle(new Vector2(TouhosuPlayfield.PLAYFIELD_SIZE.X, TouhosuPlayfield.PLAYFIELD_SIZE.Y))) 124 | return Wall.Right; 125 | 126 | return Wall.Bottom; 127 | } 128 | 129 | // Bottom/Left 130 | if (angle <= 270) 131 | { 132 | if (angle < getCornerAngle(new Vector2(0, TouhosuPlayfield.PLAYFIELD_SIZE.Y))) 133 | return Wall.Bottom; 134 | 135 | return Wall.Left; 136 | } 137 | 138 | // Left/Top 139 | if (angle < getCornerAngle(Vector2.Zero)) 140 | return Wall.Left; 141 | 142 | return Wall.Top; 143 | } 144 | 145 | private float getCornerAngle(Vector2 cornerPosition) 146 | => MathExtensions.GetSafeAngle((float)(Math.Atan2(HitObject.Y - cornerPosition.Y, HitObject.X - cornerPosition.X) * 180 / Math.PI) - 90); 147 | 148 | private static float getYPosition(Vector2 initialPosition, float finalX, float angle) 149 | => (float)(initialPosition.Y + ((finalX - initialPosition.X) / -Math.Tan(angle * Math.PI / 180))); 150 | 151 | private static float getXPosition(Vector2 initialPosition, float finalY, float angle) 152 | => (float)(initialPosition.X + ((finalY - initialPosition.Y) * -Math.Tan(angle * Math.PI / 180))); 153 | 154 | private enum Wall 155 | { 156 | Top, 157 | Right, 158 | Left, 159 | Bottom 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/DrawableInstantProjectile.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Framework.Graphics; 3 | using osu.Game.Rulesets.Objects.Drawables; 4 | using osu.Game.Rulesets.Scoring; 5 | 6 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables 7 | { 8 | public partial class DrawableInstantProjectile : DrawableProjectile 9 | { 10 | protected override bool CanHitPlayer => false; 11 | 12 | public DrawableInstantProjectile() 13 | : this(null) 14 | { 15 | } 16 | 17 | public DrawableInstantProjectile([CanBeNull] InstantProjectile h = null) 18 | : base(h) 19 | { 20 | } 21 | 22 | protected override void CheckForResult(bool userTriggered, double timeOffset) 23 | { 24 | base.CheckForResult(userTriggered, timeOffset); 25 | 26 | if (timeOffset < 0) 27 | return; 28 | 29 | ApplyResult((r, u) => r.Type = HitResult.IgnoreHit); 30 | } 31 | 32 | protected override void UpdateHitStateTransforms(ArmedState state) 33 | { 34 | switch (state) 35 | { 36 | case ArmedState.Hit: 37 | this.ScaleTo(0, 150).Then().FadeOut(); 38 | break; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/DrawableProjectile.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.Objects.Drawables; 8 | using osu.Framework.Utils; 9 | using osu.Game.Rulesets.Touhosu.Objects.Drawables.Pieces; 10 | using osu.Game.Rulesets.Touhosu.Extensions; 11 | 12 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables 13 | { 14 | public abstract partial class DrawableProjectile : DrawableTouhosuHitObject 15 | where T : Projectile 16 | { 17 | private const int hidden_distance = 70; 18 | private const int hidden_distance_buffer = 50; 19 | 20 | public bool IsGrazed = false; 21 | 22 | public readonly IBindable PositionBindable = new Bindable(); 23 | 24 | protected abstract bool CanHitPlayer { get; } 25 | 26 | public Func CheckHit; 27 | 28 | public Func CheckGrazed; 29 | public Func DistanceToPlayer; 30 | 31 | public bool HiddenApplied { get; set; } 32 | 33 | private ProjectilePiece piece; 34 | 35 | protected DrawableProjectile([CanBeNull] T h = null) 36 | : base(h) 37 | { 38 | } 39 | 40 | [BackgroundDependencyLoader] 41 | private void load() 42 | { 43 | Alpha = 0; 44 | Scale = Vector2.Zero; 45 | 46 | Origin = Anchor.Centre; 47 | Size = new Vector2(TouhosuExtensions.SPHERE_SIZE); 48 | AddInternal(piece = new ProjectilePiece("Sphere", true)); 49 | 50 | AccentColour.BindValueChanged(c => piece.AccentColour = c.NewValue); 51 | PositionBindable.BindValueChanged(p => Position = p.NewValue); 52 | } 53 | 54 | protected override void UpdateInitialTransforms() 55 | { 56 | base.UpdateInitialTransforms(); 57 | this.FadeInFromZero(); 58 | this.ScaleTo(Vector2.One, HitObject.TimePreempt); 59 | } 60 | 61 | protected override void Update() 62 | { 63 | base.Update(); 64 | 65 | if (IsHit || !HiddenApplied) 66 | return; 67 | 68 | var distance = DistanceToPlayer.Invoke(Position); 69 | 70 | if (distance > hidden_distance + hidden_distance_buffer) 71 | { 72 | piece.Alpha = 1; 73 | return; 74 | } 75 | 76 | if (distance < hidden_distance) 77 | { 78 | piece.Alpha = 0; 79 | return; 80 | } 81 | 82 | piece.Alpha = Interpolation.ValueAt((float)distance - hidden_distance, 0f, 1f, 0, hidden_distance_buffer); 83 | } 84 | 85 | private double missTime; 86 | 87 | protected override void CheckForResult(bool userTriggered, double timeOffset) 88 | { 89 | if (timeOffset < 0) 90 | return; 91 | 92 | if (!CanHitPlayer) 93 | return; 94 | 95 | if (!IsGrazed && (CheckGrazed?.Invoke(Position) ?? false)) 96 | IsGrazed = true; 97 | 98 | if (CheckHit?.Invoke(Position) ?? false) 99 | { 100 | missTime = timeOffset; 101 | ApplyResult((r, u) => r.Type = r.Judgement.MinResult); 102 | } 103 | } 104 | 105 | protected override void UpdateHitStateTransforms(ArmedState state) 106 | { 107 | base.UpdateHitStateTransforms(state); 108 | 109 | switch (state) 110 | { 111 | case ArmedState.Miss: 112 | this.Delay(missTime).FadeOut(); 113 | break; 114 | } 115 | } 116 | 117 | protected override void OnApply() 118 | { 119 | base.OnApply(); 120 | 121 | PositionBindable.BindTo(HitObject.PositionBindable); 122 | IsGrazed = false; 123 | } 124 | 125 | protected override void OnFree() 126 | { 127 | base.OnFree(); 128 | 129 | PositionBindable.UnbindFrom(HitObject.PositionBindable); 130 | } 131 | 132 | 133 | protected override double InitialLifetimeOffset => HitObject.TimePreempt; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/DrawableTouhosuHitObject.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using osu.Game.Rulesets.Objects.Drawables; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables 5 | { 6 | public abstract class DrawableTouhosuHitObject : DrawableHitObject 7 | where T : TouhosuHitObject 8 | { 9 | protected new T HitObject => (T)base.HitObject; 10 | 11 | protected DrawableTouhosuHitObject([CanBeNull] T h = null) 12 | : base(h) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/Drawables/Pieces/ProjectilePiece.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Extensions.Color4Extensions; 3 | using osu.Framework.Graphics; 4 | using osu.Framework.Graphics.Containers; 5 | using osu.Framework.Graphics.Effects; 6 | using osu.Framework.Graphics.Sprites; 7 | using osu.Framework.Graphics.Textures; 8 | using osu.Game.Graphics; 9 | using osuTK.Graphics; 10 | 11 | namespace osu.Game.Rulesets.Touhosu.Objects.Drawables.Pieces 12 | { 13 | public class ProjectilePiece : CircularContainer, IHasAccentColour 14 | { 15 | private Color4 accentColour; 16 | 17 | public Color4 AccentColour 18 | { 19 | get => accentColour; 20 | set 21 | { 22 | accentColour = value; 23 | overlay.Colour = value; 24 | 25 | if (useGlow) 26 | { 27 | EdgeEffect = new EdgeEffectParameters 28 | { 29 | Colour = value.Opacity(0.35f), 30 | Type = EdgeEffectType.Glow, 31 | Radius = 5, 32 | }; 33 | } 34 | } 35 | } 36 | 37 | private readonly string textureName; 38 | private readonly bool useGlow; 39 | private readonly Sprite texture; 40 | private readonly Sprite overlay; 41 | 42 | public ProjectilePiece(string textureName, bool useGlow) 43 | { 44 | this.textureName = textureName; 45 | this.useGlow = useGlow; 46 | 47 | RelativeSizeAxes = Axes.Both; 48 | Anchor = Anchor.Centre; 49 | Origin = Anchor.Centre; 50 | Children = new Drawable[] 51 | { 52 | texture = new Sprite 53 | { 54 | Anchor = Anchor.Centre, 55 | Origin = Anchor.Centre, 56 | RelativeSizeAxes = Axes.Both 57 | }, 58 | overlay = new Sprite 59 | { 60 | Anchor = Anchor.Centre, 61 | Origin = Anchor.Centre, 62 | RelativeSizeAxes = Axes.Both 63 | } 64 | }; 65 | 66 | if (useGlow) 67 | Masking = true; 68 | } 69 | 70 | [BackgroundDependencyLoader] 71 | private void load(TextureStore textures) 72 | { 73 | texture.Texture = textures.Get($"Projectiles/{textureName}/texture"); 74 | overlay.Texture = textures.Get($"Projectiles/{textureName}/overlay"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/InstantProjectile.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Beatmaps.ControlPoints; 3 | using osu.Game.Rulesets.Judgements; 4 | 5 | namespace osu.Game.Rulesets.Touhosu.Objects 6 | { 7 | public class InstantProjectile : Projectile 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.Touhosu/Objects/Projectile.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Bindables; 2 | using osu.Game.Rulesets.Objects.Types; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Objects 5 | { 6 | public abstract class Projectile : TouhosuHitObject, IHasComboInformation 7 | { 8 | public double TimePreempt { get; set; } = 400; 9 | 10 | public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); 11 | 12 | public int IndexInCurrentCombo 13 | { 14 | get => IndexInCurrentComboBindable.Value; 15 | set => IndexInCurrentComboBindable.Value = value; 16 | } 17 | 18 | public Bindable ComboIndexBindable { get; } = new Bindable(); 19 | 20 | public int ComboIndex 21 | { 22 | get => ComboIndexBindable.Value; 23 | set => ComboIndexBindable.Value = value; 24 | } 25 | 26 | public virtual bool NewCombo { get; set; } 27 | 28 | public Bindable LastInComboBindable { get; } = new Bindable(); 29 | 30 | public virtual bool LastInCombo 31 | { 32 | get => LastInComboBindable.Value; 33 | set => LastInComboBindable.Value = value; 34 | } 35 | 36 | public int ComboOffset { get; set; } 37 | 38 | public Bindable ComboIndexWithOffsetsBindable { get; } = new Bindable(); 39 | 40 | public int ComboIndexWithOffsets 41 | { 42 | get => ComboIndexWithOffsetsBindable.Value; 43 | set => ComboIndexWithOffsetsBindable.Value = value; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Objects/TouhosuHitObject.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.Touhosu.Objects 8 | { 9 | public class TouhosuHitObject : 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 | public float X 20 | { 21 | get => Position.X; 22 | set => Position = new Vector2(value, Position.Y); 23 | } 24 | 25 | public float Y 26 | { 27 | get => Position.Y; 28 | set => Position = new Vector2(Position.X, value); 29 | } 30 | 31 | protected override HitWindows CreateHitWindows() => HitWindows.Empty; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Replays/TouhosuAutoGenerator.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Beatmaps; 2 | using osu.Game.Replays; 3 | using osu.Game.Rulesets.Touhosu.Beatmaps; 4 | using osu.Game.Rulesets.Replays; 5 | 6 | namespace osu.Game.Rulesets.Touhosu.Replays 7 | { 8 | public class TouhosuAutoGenerator : AutoGenerator 9 | { 10 | public new TouhosuBeatmap Beatmap => (TouhosuBeatmap)base.Beatmap; 11 | 12 | public TouhosuAutoGenerator(IBeatmap beatmap) 13 | : base(beatmap) 14 | { 15 | Replay = new Replay(); 16 | } 17 | 18 | protected Replay Replay; 19 | 20 | public override Replay Generate() 21 | { 22 | Replay.Frames.Add(new TouhosuReplayFrame(0)); 23 | return Replay; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Replays/TouhosuFramedReplayInputHandler.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.Touhosu.Replays 9 | { 10 | public class TouhosuFramedReplayInputHandler : FramedReplayInputHandler 11 | { 12 | public TouhosuFramedReplayInputHandler(Replay replay) 13 | : base(replay) 14 | { 15 | } 16 | 17 | protected override bool IsImportant(TouhosuReplayFrame 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 ? Interpolation.ValueAt(CurrentTime, frame.Position, NextFrame.Position, frame.Time, NextFrame.Time) : frame.Position; 29 | } 30 | } 31 | 32 | protected override void CollectReplayInputs(List inputs) 33 | { 34 | if (Position.HasValue) 35 | { 36 | inputs.Add(new TouhosuReplayState 37 | { 38 | PressedActions = CurrentFrame?.Actions ?? new List(), 39 | Position = Position.Value 40 | }); 41 | } 42 | } 43 | 44 | public class TouhosuReplayState : ReplayState 45 | { 46 | public Vector2? Position { get; set; } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Replays/TouhosuReplayFrame.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Replays.Legacy; 4 | using osu.Game.Rulesets.Replays; 5 | using osu.Game.Rulesets.Replays.Types; 6 | using osu.Game.Rulesets.Touhosu.UI; 7 | using osuTK; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.Replays 10 | { 11 | public class TouhosuReplayFrame : ReplayFrame, IConvertibleReplayFrame 12 | { 13 | public List Actions = new List(); 14 | public Vector2 Position; 15 | public bool Focused; 16 | public bool Shooting; 17 | 18 | public TouhosuReplayFrame() 19 | { 20 | } 21 | 22 | public TouhosuReplayFrame(double time, Vector2? position = null, bool focused = false, bool shooting = false, TouhosuReplayFrame lastFrame = null) 23 | : base(time) 24 | { 25 | Position = position ?? new Vector2(TouhosuPlayfield.PLAYFIELD_SIZE.X / 2f, TouhosuPlayfield.PLAYFIELD_SIZE.Y + 100); 26 | Focused = focused; 27 | Shooting = shooting; 28 | 29 | if (Focused) 30 | Actions.Add(TouhosuAction.Focus); 31 | 32 | if (Shooting) 33 | Actions.Add(TouhosuAction.Shoot); 34 | 35 | if (lastFrame != null) 36 | { 37 | if (Position.X > lastFrame.Position.X) 38 | lastFrame.Actions.Add(TouhosuAction.MoveRight); 39 | else if (Position.X < lastFrame.Position.X) 40 | lastFrame.Actions.Add(TouhosuAction.MoveLeft); 41 | 42 | if (Position.Y > lastFrame.Position.Y) 43 | lastFrame.Actions.Add(TouhosuAction.MoveDown); 44 | else if (Position.Y < lastFrame.Position.Y) 45 | lastFrame.Actions.Add(TouhosuAction.MoveUp); 46 | } 47 | } 48 | 49 | public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null) 50 | { 51 | Position = currentFrame.Position; 52 | Focused = currentFrame.ButtonState == ReplayButtonState.Left1; 53 | Shooting = currentFrame.ButtonState == ReplayButtonState.Left2; 54 | 55 | if (Focused) 56 | Actions.Add(TouhosuAction.Focus); 57 | 58 | if (Shooting) 59 | Actions.Add(TouhosuAction.Shoot); 60 | 61 | if (lastFrame is TouhosuReplayFrame lastTouhosuFrame) 62 | { 63 | if (Position.X > lastTouhosuFrame.Position.X) 64 | lastTouhosuFrame.Actions.Add(TouhosuAction.MoveRight); 65 | else if (Position.X < lastTouhosuFrame.Position.X) 66 | lastTouhosuFrame.Actions.Add(TouhosuAction.MoveLeft); 67 | 68 | if (Position.Y > lastTouhosuFrame.Position.Y) 69 | lastTouhosuFrame.Actions.Add(TouhosuAction.MoveDown); 70 | else if (Position.Y < lastTouhosuFrame.Position.Y) 71 | lastTouhosuFrame.Actions.Add(TouhosuAction.MoveUp); 72 | } 73 | } 74 | 75 | public LegacyReplayFrame ToLegacy(IBeatmap beatmap) 76 | { 77 | ReplayButtonState state = ReplayButtonState.None; 78 | 79 | if (Actions.Contains(TouhosuAction.Focus)) state |= ReplayButtonState.Left1; 80 | if (Actions.Contains(TouhosuAction.Shoot)) state |= ReplayButtonState.Left2; 81 | 82 | return new LegacyReplayFrame(Time, Position.X, Position.Y, state); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Samples/graze.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Samples/graze.mp3 -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/0.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/25.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/50.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/75.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/HUD/HP/full.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-3.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-4.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-5.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-6.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-7.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/idle-8.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-3.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-4.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/left-5.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-1.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-2.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-3.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-4.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/Player/right-5.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/card.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Player/focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Player/focus.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Grain/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Grain/overlay.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Grain/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Grain/texture.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Knife/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Knife/overlay.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Knife/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Knife/texture.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Sphere/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Sphere/overlay.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Sphere/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/Projectiles/Sphere/texture.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Resources/Textures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EVAST9919/touhosu/aa02799dabdd87addbbd7b2f615257bcca7c9c36/osu.Game.Rulesets.Touhosu/Resources/Textures/logo.png -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Scoring/TouhosuHealthProcessor.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Judgements; 2 | using osu.Game.Rulesets.Scoring; 3 | 4 | namespace osu.Game.Rulesets.Touhosu.Scoring 5 | { 6 | public class TouhosuHealthProcessor : HealthProcessor 7 | { 8 | public bool NoRegen { get; set; } 9 | 10 | public double LossMultiplier { get; set; } = 1.0; 11 | 12 | protected override double GetHealthIncreaseFor(JudgementResult result) 13 | { 14 | double adjustment = 1.0; 15 | 16 | switch (result.Type) 17 | { 18 | case HitResult.Perfect: 19 | if (NoRegen) 20 | adjustment = 0; 21 | break; 22 | 23 | case HitResult.Miss: 24 | adjustment *= LossMultiplier; 25 | break; 26 | } 27 | 28 | return base.GetHealthIncreaseFor(result) * adjustment; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/Scoring/TouhosuHitWindows.cs: -------------------------------------------------------------------------------- 1 | using osu.Game.Rulesets.Scoring; 2 | 3 | namespace osu.Game.Rulesets.Touhosu.Scoring 4 | { 5 | public class TouhosuHitWindows : HitWindows 6 | { 7 | public override bool IsHitResultAllowed(HitResult result) 8 | { 9 | switch (result) 10 | { 11 | case HitResult.Perfect: 12 | case HitResult.Miss: 13 | return true; 14 | } 15 | 16 | return false; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/TouhosuInputManager.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Input.Bindings; 2 | using osu.Game.Rulesets.UI; 3 | using System.ComponentModel; 4 | 5 | namespace osu.Game.Rulesets.Touhosu 6 | { 7 | public class TouhosuInputManager : RulesetInputManager 8 | { 9 | public TouhosuInputManager(RulesetInfo ruleset) 10 | : base(ruleset, 0, SimultaneousBindingMode.Unique) 11 | { 12 | } 13 | } 14 | 15 | public enum TouhosuAction 16 | { 17 | [Description("Move Left")] 18 | MoveLeft, 19 | 20 | [Description("Move Right")] 21 | MoveRight, 22 | 23 | [Description("Move Up")] 24 | MoveUp, 25 | 26 | [Description("Move Down")] 27 | MoveDown, 28 | 29 | [Description("Shoot")] 30 | Shoot, 31 | 32 | [Description("Focus")] 33 | Focus 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/TouhosuRuleset.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.Touhosu.Scoring; 13 | using osu.Game.Rulesets.Touhosu.Difficulty; 14 | using osu.Game.Rulesets.Touhosu.Beatmaps; 15 | using osu.Game.Rulesets.Touhosu.Mods; 16 | using osu.Game.Rulesets.Touhosu.UI; 17 | using osu.Game.Rulesets.Configuration; 18 | using osu.Game.Configuration; 19 | using osu.Game.Rulesets.Touhosu.Configuration; 20 | using osu.Game.Overlays.Settings; 21 | using osu.Game.Rulesets.Replays.Types; 22 | using osu.Game.Rulesets.Touhosu.Replays; 23 | using osu.Framework.Allocation; 24 | using osu.Framework.Platform; 25 | 26 | namespace osu.Game.Rulesets.Touhosu 27 | { 28 | public partial class TouhosuRuleset : Ruleset 29 | { 30 | private DrawableTouhosuRuleset ruleset; 31 | 32 | public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TouhosuRulesetConfigManager(settings, RulesetInfo); 33 | 34 | public override RulesetSettingsSubsection CreateSettings() => new TouhosuSettingsSubsection(this); 35 | 36 | public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => ruleset = new DrawableTouhosuRuleset(this, beatmap, mods); 37 | 38 | public override HealthProcessor CreateHealthProcessor(double drainStartTime) 39 | { 40 | var hp = new TouhosuHealthProcessor(); 41 | ruleset.HealthProcessor = hp; 42 | return hp; 43 | } 44 | 45 | public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TouhosuBeatmapConverter(beatmap, this); 46 | 47 | public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new BeatmapProcessor(beatmap); 48 | 49 | public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TouhosuReplayFrame(); 50 | 51 | public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] 52 | { 53 | new KeyBinding(InputKey.Left, TouhosuAction.MoveLeft), 54 | new KeyBinding(InputKey.Right, TouhosuAction.MoveRight), 55 | new KeyBinding(InputKey.Up, TouhosuAction.MoveUp), 56 | new KeyBinding(InputKey.Down, TouhosuAction.MoveDown), 57 | new KeyBinding(InputKey.Z, TouhosuAction.Shoot), 58 | new KeyBinding(InputKey.Shift, TouhosuAction.Focus), 59 | }; 60 | 61 | public override IEnumerable GetModsFor(ModType type) 62 | { 63 | switch (type) 64 | { 65 | case ModType.DifficultyReduction: 66 | return new Mod[] 67 | { 68 | new TouhosuModEasy(), 69 | new TouhosuModNoFail(), 70 | new MultiMod(new TouhosuModHalfTime(), new TouhosuModDaycore()) 71 | }; 72 | 73 | case ModType.DifficultyIncrease: 74 | return new Mod[] 75 | { 76 | new TouhosuModDamageMultiplier(), 77 | new TouhosuModNoRegen(), 78 | new TouhosuModSuddenDeath(), 79 | new MultiMod(new TouhosuModDoubleTime(), new TouhosuModNightcore()), 80 | new TouhosuModHidden() 81 | }; 82 | 83 | case ModType.Automation: 84 | return new Mod[] 85 | { 86 | new TouhosuModAutoplay() 87 | }; 88 | 89 | case ModType.Fun: 90 | return new Mod[] 91 | { 92 | new MultiMod(new ModWindUp(), new ModWindDown()), 93 | }; 94 | 95 | default: 96 | return Array.Empty(); 97 | } 98 | } 99 | 100 | public override string Description => "Touhosu"; 101 | 102 | public override string ShortName => "Touhosu"; 103 | 104 | public override string PlayingVerb => "Avoiding bullets"; 105 | 106 | public override Drawable CreateIcon() => new TouhosuIcon(this); 107 | 108 | protected override IEnumerable GetValidHitResults() => new[] 109 | { 110 | HitResult.Perfect 111 | }; 112 | 113 | public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new TouhosuDifficultyCalculator(RulesetInfo, beatmap); 114 | 115 | private partial class TouhosuIcon : Sprite 116 | { 117 | private readonly TouhosuRuleset ruleset; 118 | 119 | public TouhosuIcon(TouhosuRuleset ruleset) 120 | { 121 | this.ruleset = ruleset; 122 | } 123 | 124 | [BackgroundDependencyLoader] 125 | private void load(GameHost host) 126 | { 127 | Texture = new TextureStore(host.Renderer, new TextureLoaderStore(ruleset.CreateResourceStore()), false).Get("Textures/logo"); 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/DrawableTouhosuRuleset.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Input; 2 | using osu.Game.Beatmaps; 3 | using osu.Game.Rulesets.Touhosu.Objects; 4 | using osu.Game.Rulesets.Mods; 5 | using osu.Game.Rulesets.Objects.Drawables; 6 | using osu.Game.Rulesets.UI; 7 | using System.Collections.Generic; 8 | using osu.Game.Input.Handlers; 9 | using osu.Game.Replays; 10 | using osu.Game.Rulesets.Touhosu.Replays; 11 | using osu.Game.Rulesets.Touhosu.Scoring; 12 | using osu.Game.Scoring; 13 | 14 | namespace osu.Game.Rulesets.Touhosu.UI 15 | { 16 | public class DrawableTouhosuRuleset : DrawableRuleset 17 | { 18 | public TouhosuHealthProcessor HealthProcessor 19 | { 20 | set 21 | { 22 | if (Playfield is TouhosuPlayfield p) 23 | p.ApplyHealthProcessor(value); 24 | } 25 | } 26 | 27 | public DrawableTouhosuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) 28 | : base(ruleset, beatmap, mods) 29 | { 30 | } 31 | 32 | protected override PassThroughInputManager CreateInputManager() => new TouhosuInputManager(Ruleset.RulesetInfo); 33 | 34 | protected override Playfield CreatePlayfield() => new TouhosuPlayfield(); 35 | 36 | public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TouhosuPlayfieldAdjustmentContainer(); 37 | 38 | protected override ReplayRecorder CreateReplayRecorder(Score score) => new TouhosuReplayRecorder(score, (TouhosuPlayfield)Playfield); 39 | 40 | protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TouhosuFramedReplayInputHandler(replay); 41 | 42 | public override DrawableHitObject CreateDrawableRepresentation(TouhosuHitObject h) => null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/HUD/HealthDisplay.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osu.Game.Rulesets.Touhosu.Scoring; 4 | using osu.Framework.Bindables; 5 | using osu.Framework.Graphics.Sprites; 6 | using osuTK; 7 | using osu.Framework.Graphics.Textures; 8 | using osu.Framework.Allocation; 9 | using osu.Framework.Extensions.IEnumerableExtensions; 10 | 11 | namespace osu.Game.Rulesets.Touhosu.UI.HUD 12 | { 13 | public class HealthDisplay : CompositeDrawable 14 | { 15 | private readonly TouhosuHealthProcessor healthProcessor; 16 | 17 | [Resolved] 18 | private TextureStore textures { get; set; } 19 | 20 | private readonly Sprite[] hearts = new Sprite[10]; 21 | 22 | public HealthDisplay(TouhosuHealthProcessor healthProcessor) 23 | { 24 | this.healthProcessor = healthProcessor; 25 | 26 | FillFlowContainer flow; 27 | 28 | AutoSizeAxes = Axes.Both; 29 | AddInternal(flow = new FillFlowContainer 30 | { 31 | AutoSizeAxes = Axes.Both, 32 | Direction = FillDirection.Horizontal 33 | }); 34 | 35 | for (int i = 0; i < 10; i++) 36 | hearts[i] = new Sprite { Size = new Vector2(10) }; 37 | 38 | hearts.ForEach(flow.Add); 39 | } 40 | 41 | protected override void LoadComplete() 42 | { 43 | base.LoadComplete(); 44 | 45 | healthProcessor.Health.BindValueChanged(onHealthChanged, true); 46 | } 47 | 48 | private void onHealthChanged(ValueChangedEvent health) 49 | { 50 | for (int i = 0; i < 10; i++) 51 | { 52 | if (health.NewValue == 1) 53 | hearts[i].Texture = textures.Get("HUD/HP/full"); 54 | else 55 | hearts[i].Texture = getTexture(health.NewValue, i); 56 | } 57 | } 58 | 59 | private Texture getTexture(double value, int index) 60 | { 61 | var heartHealth = value * 10 - index; 62 | 63 | if (heartHealth > 1) 64 | return textures.Get("HUD/HP/full"); 65 | 66 | if (heartHealth > 0.75f) 67 | return textures.Get("HUD/HP/75"); 68 | 69 | if (heartHealth > 0.5f) 70 | return textures.Get("HUD/HP/50"); 71 | 72 | if (heartHealth > 0.25f) 73 | return textures.Get("HUD/HP/25"); 74 | 75 | return textures.Get("HUD/HP/0"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/HUD/PlayfieldBackground.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Bindables; 3 | using osu.Framework.Graphics; 4 | using osu.Framework.Graphics.Containers; 5 | using osu.Framework.Graphics.Shapes; 6 | using osu.Game.Rulesets.Touhosu.Configuration; 7 | using osuTK.Graphics; 8 | 9 | namespace osu.Game.Rulesets.Touhosu.UI.HUD 10 | { 11 | public class PlayfieldBackground : CompositeDrawable 12 | { 13 | private readonly Bindable bgDim = new Bindable(); 14 | 15 | [Resolved(canBeNull: true)] 16 | private TouhosuRulesetConfigManager config { get; set; } 17 | 18 | private readonly Box bg; 19 | 20 | public PlayfieldBackground() 21 | { 22 | RelativeSizeAxes = Axes.Both; 23 | AddInternal(bg = new Box 24 | { 25 | RelativeSizeAxes = Axes.Both, 26 | Colour = Color4.Black, 27 | AlwaysPresent = true, 28 | }); 29 | } 30 | 31 | [BackgroundDependencyLoader] 32 | private void load() 33 | { 34 | config?.BindWith(TouhosuRulesetSetting.PlayfieldDim, bgDim); 35 | } 36 | 37 | protected override void LoadComplete() 38 | { 39 | base.LoadComplete(); 40 | bgDim.BindValueChanged(dim => onDimChanged(dim.NewValue), true); 41 | } 42 | 43 | private void onDimChanged(double newDim) 44 | { 45 | bg.Alpha = (float)newDim; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/Objects/Card.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Allocation; 2 | using osu.Framework.Graphics.Sprites; 3 | using osu.Framework.Graphics.Textures; 4 | using osuTK; 5 | using osu.Framework.Graphics; 6 | 7 | namespace osu.Game.Rulesets.Touhosu.UI.Objects 8 | { 9 | public class Card : Sprite 10 | { 11 | public int Strength { get; set; } 12 | 13 | public Card() 14 | { 15 | Size = new Vector2(8, 20); 16 | Origin = Anchor.BottomCentre; 17 | Alpha = 0.5f; 18 | } 19 | 20 | [BackgroundDependencyLoader] 21 | private void load(TextureStore textures) 22 | { 23 | Texture = textures.Get("Player/card"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/Objects/CardsController.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using osu.Game.Rulesets.UI; 7 | 8 | namespace osu.Game.Rulesets.Touhosu.UI.Objects 9 | { 10 | public class CardsController : CompositeDrawable 11 | { 12 | private const float speed_per_field = 300; 13 | 14 | public override bool RemoveCompletedTransforms => true; 15 | 16 | public HitObjectContainer HitObjects { get; set; } 17 | 18 | public CardsController() 19 | { 20 | RelativeSizeAxes = Axes.Both; 21 | } 22 | 23 | public void Shoot(Vector2 position, bool isFocused) 24 | { 25 | Card left; 26 | Card right; 27 | 28 | AddRangeInternal(new Drawable[] 29 | { 30 | left = new Card 31 | { 32 | Position = new Vector2(position.X + 5, position.Y - 15), 33 | Strength = isFocused ? 2 : 1 34 | }, 35 | right = new Card 36 | { 37 | Position = new Vector2(position.X - 5, position.Y - 15), 38 | Strength = isFocused ? 2 : 1 39 | }, 40 | }); 41 | 42 | var time = speed_per_field * (position.Y / TouhosuPlayfield.PLAYFIELD_SIZE.Y); 43 | left.MoveToY(0, time).Expire(); 44 | right.MoveToY(0, time).Expire(); 45 | } 46 | 47 | public List GetCards() => InternalChildren.OfType().Where(c => c.IsAlive).ToList(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/Objects/FocusAnimation.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using osu.Framework.Graphics.Sprites; 5 | using osu.Framework.Allocation; 6 | using osu.Framework.Graphics.Textures; 7 | 8 | namespace osu.Game.Rulesets.Touhosu.UI.Objects 9 | { 10 | public class FocusAnimation : CompositeDrawable 11 | { 12 | private readonly Sprite focus1; 13 | private readonly Sprite focus2; 14 | 15 | public FocusAnimation() 16 | { 17 | AddRangeInternal(new Drawable[] 18 | { 19 | focus1 = new Sprite 20 | { 21 | Anchor = Anchor.Centre, 22 | Origin = Anchor.Centre, 23 | Size = new Vector2(60), 24 | Scale = new Vector2(0.7f), 25 | Alpha = 0, 26 | AlwaysPresent = true, 27 | }, 28 | focus2 = new Sprite 29 | { 30 | Anchor = Anchor.Centre, 31 | Origin = Anchor.Centre, 32 | Size = new Vector2(60), 33 | Scale = new Vector2(0.7f), 34 | Alpha = 0, 35 | AlwaysPresent = true, 36 | } 37 | }); 38 | } 39 | 40 | [BackgroundDependencyLoader] 41 | private void load(TextureStore textures) 42 | { 43 | focus1.Texture = focus2.Texture = textures.Get("Player/focus"); 44 | } 45 | 46 | protected override void LoadComplete() 47 | { 48 | base.LoadComplete(); 49 | 50 | focus1.Spin(4000, RotationDirection.Clockwise); 51 | focus2.Spin(4000, RotationDirection.Counterclockwise); 52 | } 53 | 54 | public void Focus() 55 | { 56 | focus1.ScaleTo(1, 200, Easing.Out); 57 | focus1.FadeIn(200); 58 | 59 | focus2.ScaleTo(1, 200, Easing.Out); 60 | focus2.FadeIn(200); 61 | } 62 | 63 | public void FocusLost() 64 | { 65 | focus1.ScaleTo(0.7f, 200, Easing.Out); 66 | focus1.FadeOut(200); 67 | 68 | focus2.ScaleTo(0.7f, 200, Easing.Out); 69 | focus2.FadeOut(200); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/Objects/PlayerAnimation.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.Touhosu.UI.Objects 7 | { 8 | public class PlayerAnimation : TextureAnimation 9 | { 10 | private const double duration = 100; 11 | 12 | private readonly PlayerState state; 13 | 14 | public PlayerAnimation(PlayerState state) 15 | { 16 | this.state = state; 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 (state) 27 | { 28 | case PlayerState.Idle: 29 | for (int i = 1; i <= 8; i++) 30 | AddFrame(textures.Get($"Player/Player/idle-{i}"), duration); 31 | break; 32 | 33 | case PlayerState.Left: 34 | for (int i = 1; i <= 5; i++) 35 | AddFrame(textures.Get($"Player/Player/left-{i}"), duration); 36 | break; 37 | 38 | case PlayerState.Right: 39 | for (int i = 1; i <= 5; i++) 40 | AddFrame(textures.Get($"Player/Player/right-{i}"), duration); 41 | break; 42 | } 43 | } 44 | } 45 | 46 | public enum PlayerState 47 | { 48 | Idle, 49 | Left, 50 | Right 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/Objects/TouhosuPlayer.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics.Containers; 2 | using osu.Framework.Graphics; 3 | using osuTK; 4 | using System; 5 | using osuTK.Graphics; 6 | using osu.Framework.Input.Bindings; 7 | using System.Collections.Generic; 8 | using osu.Game.Rulesets.UI; 9 | using osu.Framework.Bindables; 10 | using osu.Game.Rulesets.Touhosu.Extensions; 11 | using osu.Game.Rulesets.Touhosu.Replays; 12 | using osu.Framework.Input.Events; 13 | 14 | namespace osu.Game.Rulesets.Touhosu.UI.Objects 15 | { 16 | public partial class TouhosuPlayer : CompositeDrawable, IKeyBindingHandler 17 | { 18 | public static readonly float HITBOX_SIZE = 7; 19 | 20 | public static readonly float GRAZE_SIZE = HITBOX_SIZE * 3; 21 | 22 | private const float base_speed = 0.2f; 23 | private const float shoot_delay = 80; 24 | 25 | private float speedMultiplier = 1; 26 | 27 | private readonly Bindable state = new Bindable(PlayerState.Idle); 28 | 29 | private HitObjectContainer hitObjects; 30 | 31 | public HitObjectContainer HitObjects 32 | { 33 | get => hitObjects; 34 | set 35 | { 36 | hitObjects = value; 37 | cardsController.HitObjects = value; 38 | } 39 | } 40 | 41 | public double SpeedMultiplier { get; set; } = 1.0; 42 | 43 | public override bool RemoveCompletedTransforms => false; 44 | 45 | private int horizontalDirection; 46 | private int verticalDirection; 47 | 48 | public readonly Container Player; 49 | private readonly FocusAnimation focus; 50 | private readonly CardsController cardsController; 51 | private readonly Container animationContainer; 52 | 53 | public TouhosuPlayer() 54 | { 55 | RelativeSizeAxes = Axes.Both; 56 | AddRangeInternal(new Drawable[] 57 | { 58 | cardsController = new CardsController(), 59 | Player = new Container 60 | { 61 | Origin = Anchor.Centre, 62 | Position = new Vector2(TouhosuPlayfield.PLAYFIELD_SIZE.X / 2f, TouhosuPlayfield.PLAYFIELD_SIZE.Y - 20), 63 | Children = new Drawable[] 64 | { 65 | animationContainer = new Container 66 | { 67 | Anchor = Anchor.Centre, 68 | Origin = Anchor.Centre, 69 | Size = new Vector2(23.25f, 33.75f), 70 | }, 71 | focus = new FocusAnimation() 72 | } 73 | } 74 | }); 75 | } 76 | 77 | protected override void LoadComplete() 78 | { 79 | base.LoadComplete(); 80 | state.BindValueChanged(onStateChanged, true); 81 | } 82 | 83 | private bool isDead; 84 | 85 | public void Die() 86 | { 87 | isDead = true; 88 | onFocusReleased(); 89 | onShootReleased(); 90 | Player.FadeOut(500, Easing.Out); 91 | } 92 | 93 | public void PlayMissAnimation() 94 | { 95 | if (isDead) 96 | return; 97 | 98 | animationContainer.FlashColour(Color4.Red, 1000, Easing.OutQuint); 99 | } 100 | 101 | public Vector2 PlayerPosition() => Player.Position; 102 | 103 | public bool OnPressed(KeyBindingPressEvent e) 104 | { 105 | if (isDead) 106 | return true; 107 | 108 | switch (e.Action) 109 | { 110 | case TouhosuAction.MoveLeft: 111 | horizontalDirection--; 112 | return true; 113 | 114 | case TouhosuAction.MoveRight: 115 | horizontalDirection++; 116 | return true; 117 | 118 | case TouhosuAction.MoveUp: 119 | verticalDirection--; 120 | return true; 121 | 122 | case TouhosuAction.MoveDown: 123 | verticalDirection++; 124 | return true; 125 | 126 | case TouhosuAction.Focus: 127 | onFocusPressed(); 128 | return true; 129 | 130 | case TouhosuAction.Shoot: 131 | onShootPressed(); 132 | return true; 133 | } 134 | 135 | return false; 136 | } 137 | 138 | public void OnReleased(KeyBindingReleaseEvent e) 139 | { 140 | if (isDead) 141 | return; 142 | 143 | switch (e.Action) 144 | { 145 | case TouhosuAction.MoveLeft: 146 | horizontalDirection++; 147 | return; 148 | 149 | case TouhosuAction.MoveRight: 150 | horizontalDirection--; 151 | return; 152 | 153 | case TouhosuAction.MoveUp: 154 | verticalDirection++; 155 | return; 156 | 157 | case TouhosuAction.MoveDown: 158 | verticalDirection--; 159 | return; 160 | 161 | case TouhosuAction.Focus: 162 | onFocusReleased(); 163 | return; 164 | 165 | case TouhosuAction.Shoot: 166 | onShootReleased(); 167 | return; 168 | } 169 | } 170 | 171 | protected override void Update() 172 | { 173 | base.Update(); 174 | 175 | var replayState = (GetContainingInputManager().CurrentState as RulesetInputManagerInputState)?.LastReplayState as TouhosuFramedReplayInputHandler.TouhosuReplayState; 176 | 177 | if (replayState?.Position.Value != null) 178 | { 179 | Player.Position = replayState.Position.Value; 180 | } 181 | else 182 | { 183 | move(Clock.ElapsedFrameTime * SpeedMultiplier, horizontalDirection, verticalDirection); 184 | } 185 | 186 | if (isDead) 187 | return; 188 | 189 | updatePlayerState(); 190 | } 191 | 192 | private void move(double elapsedTime, int horizontalDirection, int verticalDirection) 193 | { 194 | var movingH = horizontalDirection != 0; 195 | var movingV = verticalDirection != 0; 196 | 197 | if (!movingV && !movingH) 198 | return; 199 | 200 | // Diagonal movement 201 | if (movingV && movingH) 202 | { 203 | var oldX = Player.X; 204 | var oldY = Player.Y; 205 | var newX = oldX + Math.Sign(horizontalDirection) * elapsedTime * base_speed * speedMultiplier; 206 | var newY = oldY + Math.Sign(verticalDirection) * elapsedTime * base_speed * speedMultiplier; 207 | 208 | var expectedDistance = Math.Abs(newX - oldX); 209 | var realDistance = Vector2.Distance(new Vector2(oldX, oldY), new Vector2((float)newX, (float)newY)); 210 | var offset = Math.Sqrt(MathExtensions.Pow(expectedDistance - realDistance) / 2); 211 | 212 | newX += (horizontalDirection > 0 ? -1 : 1) * offset; 213 | newY += (verticalDirection > 0 ? -1 : 1) * offset; 214 | 215 | newX = Math.Clamp(newX, animationContainer.Width / 2, TouhosuPlayfield.PLAYFIELD_SIZE.X - animationContainer.Width / 2); 216 | newY = Math.Clamp(newY, animationContainer.Height / 2, TouhosuPlayfield.PLAYFIELD_SIZE.Y - animationContainer.Height / 2); 217 | 218 | Player.Position = new Vector2((float)newX, (float)newY); 219 | return; 220 | } 221 | 222 | if (movingV) 223 | { 224 | var position = Math.Clamp(Player.Y + Math.Sign(verticalDirection) * elapsedTime * base_speed * speedMultiplier, animationContainer.Height / 2, TouhosuPlayfield.PLAYFIELD_SIZE.Y - animationContainer.Height / 2); 225 | Player.Y = (float)position; 226 | return; 227 | } 228 | 229 | if (movingH) 230 | { 231 | var position = Math.Clamp(Player.X + Math.Sign(horizontalDirection) * elapsedTime * base_speed * speedMultiplier, animationContainer.Width / 2, TouhosuPlayfield.PLAYFIELD_SIZE.X - animationContainer.Width / 2); 232 | Player.X = (float)position; 233 | } 234 | } 235 | 236 | public List GetCards() => cardsController.GetCards(); 237 | 238 | private bool isFocused; 239 | 240 | private void onFocusPressed() 241 | { 242 | isFocused = true; 243 | speedMultiplier = 0.5f; 244 | focus.Focus(); 245 | } 246 | 247 | private void onFocusReleased() 248 | { 249 | isFocused = false; 250 | speedMultiplier = 1; 251 | focus.FocusLost(); 252 | } 253 | 254 | private void onShootPressed() 255 | { 256 | cardsController.Shoot(PlayerPosition(), isFocused); 257 | Scheduler.AddDelayed(onShootPressed, shoot_delay); 258 | } 259 | 260 | private void onShootReleased() 261 | { 262 | Scheduler.CancelDelayedTasks(); 263 | } 264 | 265 | private void updatePlayerState() 266 | { 267 | if (horizontalDirection == 1) 268 | { 269 | state.Value = PlayerState.Right; 270 | return; 271 | } 272 | 273 | if (horizontalDirection == -1) 274 | { 275 | state.Value = PlayerState.Left; 276 | return; 277 | } 278 | 279 | state.Value = PlayerState.Idle; 280 | } 281 | 282 | private void onStateChanged(ValueChangedEvent s) 283 | { 284 | animationContainer.Child = new PlayerAnimation(s.NewValue); 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/TouhosuPlayfield.cs: -------------------------------------------------------------------------------- 1 | using osu.Framework.Graphics; 2 | using osu.Framework.Graphics.Containers; 3 | using osu.Game.Rulesets.Touhosu.UI.Objects; 4 | using osu.Game.Rulesets.UI; 5 | using osuTK; 6 | using osu.Game.Rulesets.Touhosu.UI.HUD; 7 | using osu.Game.Rulesets.Objects.Drawables; 8 | using osuTK.Graphics; 9 | using osu.Framework.Graphics.Effects; 10 | using osu.Framework.Extensions.Color4Extensions; 11 | using osu.Game.Rulesets.Touhosu.Scoring; 12 | using osu.Framework.Allocation; 13 | using osu.Game.Rulesets.Objects; 14 | using osu.Game.Rulesets.Touhosu.Objects; 15 | using osu.Game.Rulesets.Touhosu.Objects.Drawables; 16 | using osu.Framework.Audio.Sample; 17 | using osu.Framework.Graphics.Shapes; 18 | 19 | namespace osu.Game.Rulesets.Touhosu.UI 20 | { 21 | public partial class TouhosuPlayfield : Playfield 22 | { 23 | public static readonly Vector2 FULL_SIZE = new Vector2(512, 384); 24 | public static readonly Vector2 PLAYFIELD_SIZE = new Vector2(307, 384); 25 | 26 | public double SpeedMultiplier 27 | { 28 | get => Player.SpeedMultiplier; 29 | set => Player.SpeedMultiplier = value; 30 | } 31 | 32 | private Sample grazeSample; 33 | 34 | public TouhosuPlayer Player; 35 | 36 | public TouhosuPlayfield() 37 | { 38 | InternalChildren = new Drawable[] 39 | { 40 | new Container 41 | { 42 | Anchor = Anchor.Centre, 43 | Origin = Anchor.Centre, 44 | RelativeSizeAxes = Axes.Both, 45 | Masking = true, 46 | CornerRadius = 7, 47 | EdgeEffect = new EdgeEffectParameters 48 | { 49 | Hollow = true, 50 | Radius = 10, 51 | Colour = Color4.Black.Opacity(0.4f), 52 | Type = EdgeEffectType.Shadow, 53 | }, 54 | Child = new Box 55 | { 56 | RelativeSizeAxes = Axes.Both, 57 | Alpha = 0, 58 | AlwaysPresent = true 59 | } 60 | }, 61 | new Container 62 | { 63 | RelativeSizeAxes = Axes.Both, 64 | Padding = new MarginPadding(1), 65 | Child = new Container 66 | { 67 | Anchor = Anchor.Centre, 68 | Origin = Anchor.Centre, 69 | RelativeSizeAxes = Axes.Both, 70 | Masking = true, 71 | CornerRadius = 7, 72 | Children = new Drawable[] 73 | { 74 | new PlayfieldBackground(), 75 | Player = new TouhosuPlayer(), 76 | HitObjectContainer 77 | } 78 | } 79 | }, 80 | new Container 81 | { 82 | Anchor = Anchor.Centre, 83 | Origin = Anchor.Centre, 84 | RelativeSizeAxes = Axes.Both, 85 | Masking = true, 86 | CornerRadius = 7, 87 | BorderThickness = 1.2f, 88 | BorderColour = Color4.White, 89 | Child = new Box 90 | { 91 | RelativeSizeAxes = Axes.Both, 92 | Alpha = 0, 93 | AlwaysPresent = true 94 | } 95 | }, 96 | }; 97 | 98 | Player.HitObjects = HitObjectContainer; 99 | } 100 | 101 | [BackgroundDependencyLoader(true)] 102 | private void load(ISampleStore samples) 103 | { 104 | RegisterPool(300, 1500); 105 | RegisterPool(300, 600); 106 | grazeSample = samples.Get("graze"); 107 | } 108 | 109 | protected override void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) 110 | { 111 | base.OnNewDrawableHitObject(drawableHitObject); 112 | 113 | switch (drawableHitObject) 114 | { 115 | case DrawableAngeledProjectile projectile: 116 | projectile.CheckHit += checkHit; 117 | projectile.DistanceToPlayer += getDistanceToPlayer; 118 | projectile.CheckGrazed += checkGrazed; 119 | break; 120 | } 121 | } 122 | 123 | private bool checkHit(Vector2 pos) 124 | { 125 | var isHit = Vector2.Distance(Player.PlayerPosition(), pos) < TouhosuPlayer.HITBOX_SIZE; 126 | 127 | if (isHit) 128 | Player.PlayMissAnimation(); 129 | 130 | return isHit; 131 | } 132 | 133 | private bool checkGrazed(Vector2 pos) 134 | { 135 | var isGrazed = Vector2.Distance(Player.PlayerPosition(), pos) < TouhosuPlayer.GRAZE_SIZE; 136 | 137 | if (isGrazed) 138 | { 139 | grazeSample?.Play(); 140 | } 141 | 142 | return isGrazed; 143 | } 144 | 145 | private float getDistanceToPlayer(Vector2 pos) => Vector2.Distance(Player.PlayerPosition(), pos); 146 | 147 | public void ApplyHealthProcessor(TouhosuHealthProcessor p) 148 | { 149 | p.Failed += onFail; 150 | } 151 | 152 | private bool onFail() 153 | { 154 | Player.Die(); 155 | return true; 156 | } 157 | 158 | protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TouhosuHitObjectLifetimeEntry(hitObject); 159 | 160 | private class TouhosuHitObjectLifetimeEntry : HitObjectLifetimeEntry 161 | { 162 | public TouhosuHitObjectLifetimeEntry(HitObject hitObject) 163 | : base(hitObject) 164 | { 165 | } 166 | 167 | protected override double InitialLifetimeOffset 168 | { 169 | get 170 | { 171 | if (HitObject is Projectile projectile) 172 | return projectile.TimePreempt; 173 | 174 | return base.InitialLifetimeOffset; 175 | } 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/TouhosuPlayfieldAdjustmentContainer.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.Touhosu.UI 7 | { 8 | public class TouhosuPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer 9 | { 10 | protected override Container Content => content; 11 | private readonly Container content; 12 | 13 | private const float playfield_size_adjust = 0.83f; 14 | 15 | public TouhosuPlayfieldAdjustmentContainer() 16 | { 17 | Anchor = Anchor.Centre; 18 | Origin = Anchor.Centre; 19 | 20 | Size = new Vector2(playfield_size_adjust); 21 | 22 | InternalChild = new Container 23 | { 24 | Anchor = Anchor.Centre, 25 | Origin = Anchor.Centre, 26 | RelativeSizeAxes = Axes.Both, 27 | FillMode = FillMode.Fit, 28 | FillAspectRatio = 4f / 5, 29 | Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both } 30 | }; 31 | } 32 | 33 | /// 34 | /// A which scales its content relative to a target width. 35 | /// 36 | private class ScalingContainer : Container 37 | { 38 | protected override void Update() 39 | { 40 | base.Update(); 41 | Scale = new Vector2(Parent.ChildSize.X / TouhosuPlayfield.PLAYFIELD_SIZE.X); 42 | Size = Vector2.Divide(Vector2.One, Scale); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/TouhosuReplayRecorder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using osu.Game.Rulesets.Touhosu.Replays; 3 | using osu.Game.Rulesets.Replays; 4 | using osu.Game.Rulesets.UI; 5 | using osuTK; 6 | using osu.Game.Scoring; 7 | 8 | namespace osu.Game.Rulesets.Touhosu.UI 9 | { 10 | public class TouhosuReplayRecorder : ReplayRecorder 11 | { 12 | private readonly TouhosuPlayfield playfield; 13 | 14 | public TouhosuReplayRecorder(Score score, TouhosuPlayfield playfield) 15 | : base(score) 16 | { 17 | this.playfield = playfield; 18 | } 19 | 20 | protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) 21 | => new TouhosuReplayFrame(Time.Current, playfield.Player.PlayerPosition(), actions.Contains(TouhosuAction.Focus), actions.Contains(TouhosuAction.Shoot), previousFrame as TouhosuReplayFrame); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/UI/TouhosuSettingsSubsection.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.Touhosu.Configuration; 6 | 7 | namespace osu.Game.Rulesets.Touhosu.UI 8 | { 9 | public class TouhosuSettingsSubsection : RulesetSettingsSubsection 10 | { 11 | protected override LocalisableString Header => "Touhosu"; 12 | 13 | public TouhosuSettingsSubsection(Ruleset ruleset) 14 | : base(ruleset) 15 | { 16 | } 17 | 18 | [BackgroundDependencyLoader] 19 | private void load() 20 | { 21 | var config = (TouhosuRulesetConfigManager)Config; 22 | 23 | Children = new Drawable[] 24 | { 25 | new SettingsSlider 26 | { 27 | LabelText = "Playfield dim", 28 | Current = config.GetBindable(TouhosuRulesetSetting.PlayfieldDim), 29 | KeyboardStep = 0.01f, 30 | DisplayAsPercentage = true 31 | } 32 | }; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /osu.Game.Rulesets.Touhosu/osu.Game.Rulesets.Touhosu.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | osu.Game.Rulesets.Touhosu 5 | Library 6 | AnyCPU 7 | osu.Game.Rulesets.Touhosu 8 | 10 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------