├── .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 |
--------------------------------------------------------------------------------