├── release-notes.txt
├── lib
├── Clipper
│ ├── README
│ ├── clipper_library.dll
│ └── License.txt
├── Piwik.Tracker
│ └── Piwik.Tracker.dll
└── SoundCloud.API.Clent
│ └── SoundCloud.API.Client.dll
├── .paket
├── paket.bootstrapper.exe
└── paket.targets
├── src
└── TurntNinja
│ ├── Content
│ ├── Images
│ │ ├── icon.icns
│ │ ├── icon.ico
│ │ └── DefaultSkin.png
│ ├── Songs
│ │ ├── Wayfarer.mp3
│ │ ├── Never-Stop.mp3
│ │ └── song attribution.txt
│ ├── Fonts
│ │ ├── Oswald-Bold.ttf
│ │ ├── Oswald-Light.ttf
│ │ ├── Oswald-Regular.ttf
│ │ ├── Open Sans
│ │ │ ├── OpenSans-Bold.ttf
│ │ │ ├── OpenSans-Light.ttf
│ │ │ ├── OpenSans-Italic.ttf
│ │ │ ├── OpenSans-Regular.ttf
│ │ │ ├── OpenSans-Semibold.ttf
│ │ │ ├── OpenSans-BoldItalic.ttf
│ │ │ ├── OpenSans-ExtraBold.ttf
│ │ │ ├── OpenSans-LightItalic.ttf
│ │ │ ├── OpenSans-ExtraBoldItalic.ttf
│ │ │ └── OpenSans-SemiboldItalic.ttf
│ │ ├── Ostrich Sans
│ │ │ ├── OstrichSans-Black.otf
│ │ │ └── Open Font License.markdown
│ │ └── OFL.txt
│ └── Shaders
│ │ ├── simple.fs
│ │ ├── simple.vs
│ │ └── colour.vs
│ ├── CSCore.dll.config
│ ├── Audio
│ ├── SongCache.cs
│ ├── IAudioAnalyser.cs
│ ├── PlaylistHelper.cs
│ ├── SongBase.cs
│ ├── Song.cs
│ ├── AcoustIDCSCore.cs
│ └── AudioFeatures.cs
│ ├── paket.references
│ ├── FileSystem
│ ├── FileBrowserEntry.cs
│ ├── IFileSystem.cs
│ ├── RecentFileSystem.cs
│ ├── SoundCloudFileSystem.cs
│ └── LocalFileSystem.cs
│ ├── TurntNinja.nuspec
│ ├── Game
│ ├── HighScore.cs
│ ├── Difficulty.cs
│ ├── OnsetCollection.cs
│ ├── Player.cs
│ └── StageAudio.cs
│ ├── Settings.cs
│ ├── OpenTK.dll.config
│ ├── Properties
│ ├── AssemblyInfo.cs
│ └── Settings.settings
│ ├── Core
│ ├── HUSLColor.cs
│ └── Settings
│ │ ├── PropertySettings.cs
│ │ └── JsonSettings.cs
│ ├── app.manifest
│ ├── Logging
│ ├── SentryErrorReporting.cs
│ └── PiwikAnalytics.cs
│ ├── App.config
│ ├── GUI
│ ├── FirstRunScene.cs
│ ├── ChooseSongScene.cs
│ ├── GameScene.cs
│ ├── EndGameScene.cs
│ ├── LoadingScene.cs
│ └── MenuScene.cs
│ ├── Program.cs
│ └── Generation
│ └── StageGeometryBuilder.cs
├── appveyor.yml
├── turnt-ninja.sln.DotSettings
├── README.md
├── scripts
├── package-files.ps1
├── package-files-new.ps1
├── create-squirrel-package.ps1
└── Butler.fs
├── .travis.yml
├── docs
├── licenses
│ ├── QuickFont.txt
│ ├── ColorMine.txt
│ ├── Substructio.txt
│ ├── Clipper.txt
│ ├── OpenTK.txt
│ └── CSCore.md
└── souncloud-api-v2-notes.md
├── paket.dependencies
├── turnt-ninja.sln
├── paket.lock
├── .gitattributes
└── .gitignore
/release-notes.txt:
--------------------------------------------------------------------------------
1 | Release notes go here
--------------------------------------------------------------------------------
/lib/Clipper/README:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/lib/Clipper/README
--------------------------------------------------------------------------------
/.paket/paket.bootstrapper.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/.paket/paket.bootstrapper.exe
--------------------------------------------------------------------------------
/lib/Clipper/clipper_library.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/lib/Clipper/clipper_library.dll
--------------------------------------------------------------------------------
/lib/Piwik.Tracker/Piwik.Tracker.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/lib/Piwik.Tracker/Piwik.Tracker.dll
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Images/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Images/icon.icns
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Images/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Images/icon.ico
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Songs/Wayfarer.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Songs/Wayfarer.mp3
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Oswald-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Oswald-Bold.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Oswald-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Oswald-Light.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Images/DefaultSkin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Images/DefaultSkin.png
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Songs/Never-Stop.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Songs/Never-Stop.mp3
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Oswald-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Oswald-Regular.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Shaders/simple.fs:
--------------------------------------------------------------------------------
1 |
2 | in vec4 vs_color;
3 | out vec4 color;
4 |
5 | void main(void)
6 | {
7 | color = vs_color;
8 | }
9 |
--------------------------------------------------------------------------------
/lib/SoundCloud.API.Clent/SoundCloud.API.Client.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/lib/SoundCloud.API.Clent/SoundCloud.API.Client.dll
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Bold.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Light.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Italic.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-Semibold.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-LightItalic.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Ostrich Sans/OstrichSans-Black.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Ostrich Sans/OstrichSans-Black.otf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-SemiboldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opcon/turnt-ninja/HEAD/src/TurntNinja/Content/Fonts/Open Sans/OpenSans-SemiboldItalic.ttf
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Songs/song attribution.txt:
--------------------------------------------------------------------------------
1 | Never-Stop - Whatfunk - http://www.whatfunk.com/ - CC0 1.0 Universal (CC0 1.0)
2 | Wayfarer - Whatfunk - http://www.whatfunk.com/ - CC0 1.0 Universal (CC0 1.0)
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Shaders/simple.vs:
--------------------------------------------------------------------------------
1 |
2 | uniform mat4 mvp;
3 | uniform vec4 in_color;
4 |
5 | in vec2 in_position;
6 | out vec4 vs_color;
7 |
8 | void main(void)
9 | {
10 | gl_Position = mvp * vec4(in_position.xy, 0., 1.0);
11 | vs_color = in_color;
12 | }
13 |
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Shaders/colour.vs:
--------------------------------------------------------------------------------
1 | #version 130
2 |
3 | uniform mat4 mvp;
4 |
5 | in vec2 in_position;
6 | in vec4 in_color;
7 | out vec4 vs_color;
8 |
9 | void main(void)
10 | {
11 | gl_Position = mvp * vec4(in_position.xy, 0., 1.0);
12 | vs_color = in_color;
13 | }
--------------------------------------------------------------------------------
/src/TurntNinja/CSCore.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/SongCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace TurntNinja.Audio
8 | {
9 | class SongCache
10 | {
11 | int _maximumSongCount;
12 | List _songs;
13 |
14 | public SongCache()
15 | {
16 |
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/TurntNinja/paket.references:
--------------------------------------------------------------------------------
1 | ColorMine
2 | CSCore
3 | CSCore.OSX
4 | DeltaCompressionDotNet
5 | Gwen
6 | Gwen.Renderer.OpenTK
7 | HUSL
8 | LiteDB
9 | MathNet.Numerics
10 | Mono.Cecil
11 | Newtonsoft.Json
12 | OnsetDetection
13 | OpenTK content: none
14 | QuickFont.Desktop
15 | SharpFont import_targets: false
16 | SharpFont.Dependencies
17 | Splat
18 | squirrel.windows
19 | TagLib.Portable
20 | SharpRaven
21 | AcoustID.NET
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 0.11.{build}-appveyor
2 | image: Visual Studio 2015
3 | environment:
4 | BUTLER_API_KEY:
5 | secure: 1YditInrUO36VFxX37IzVWUC16FEwJ238DsqhKpgMxuRzyTCR8MTFMbEs6LiNY1f
6 | configuration:
7 | - Debug
8 | - Release
9 | build:
10 | parallel: true
11 | verbosity: minimal
12 | build_script:
13 | - cmd: .\build.cmd mode=%CONFIGURATION%
14 | after_build:
15 | - cmd: .\build.cmd PushArtifactsAndItchBuilds mode=%CONFIGURATION%
16 |
--------------------------------------------------------------------------------
/turnt-ninja.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/IAudioAnalyser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace BeatDetection.Audio
8 | {
9 | interface IAudioAnalyser
10 | {
11 | List ExtractOnsets(string audioFilePath);
12 | bool SongAnalysed(string audioFilePath);
13 | }
14 |
15 | struct AnalysisArguments
16 | {
17 | public string CSVDirectory;
18 | public string AudioFilePath;
19 | public string InitialOutputSuffix;
20 | public string DesiredOutputSuffix;
21 | public float Correction;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/TurntNinja/FileSystem/FileBrowserEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace TurntNinja.FileSystem
8 | {
9 | public struct FileBrowserEntry
10 | {
11 | public string Path;
12 | public string Name;
13 | public FileBrowserEntryType EntryType;
14 | }
15 |
16 | [Flags]
17 | public enum FileBrowserEntryType
18 | {
19 | Song = 1 << 0,
20 | Directory = 1 << 1,
21 | Drive = 1 << 2,
22 | Special = 1 << 3,
23 | Separator = 1 << 4,
24 | Plugin = 1 << 5
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Turnt Ninja [](https://travis-ci.org/opcon/turnt-ninja) [](https://ci.appveyor.com/project/opcon/turnt-ninja)
2 | ===========
3 |
4 | A beat-fighting-ninja-like-get-turnt rhythm game inspired by the wonderful Super Hexagon by Terry Cavanagh.
5 |
6 | # Screenshots
7 |
8 | 
9 | 
10 |
11 | # Gameplay Videos
12 | [](https://streamable.com/5b3b)
13 |
14 | # License
15 |
16 | GPL, see the LICENSE file in the repository root
17 |
--------------------------------------------------------------------------------
/src/TurntNinja/TurntNinja.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | turnt_ninja
5 | @build.number@
6 | @project@
7 | @authors@
8 | $author$
9 | https://github.com/opcon/turnt-ninja/blob/master/LICENSE
10 | https://github.com/opcon/turnt-ninja
11 | false
12 | https://github.com/opcon/turnt-ninja
13 | @releaseNotes@
14 | Copyright 2016
15 |
16 | @files@
17 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/PlaylistHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace TurntNinja.Audio
9 | {
10 | public static class PlaylistHelper
11 | {
12 | public static List LoadPlaylist(string playlistFile)
13 | {
14 | StreamReader file = File.OpenText(playlistFile);
15 | var lines = new List();
16 | string line;
17 | while ((line = file.ReadLine()) != null)
18 | {
19 | //check that this line is not a comment
20 | if (line.StartsWith("#")) continue;
21 | lines.Add(line);
22 | }
23 |
24 | return lines;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/TurntNinja/FileSystem/IFileSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using CSCore;
8 | using TurntNinja.Audio;
9 |
10 |
11 | namespace TurntNinja.FileSystem
12 | {
13 | public interface IFileSystem
14 | {
15 | List FileSystemCollection { get; set; }
16 | ReadOnlyCollection FileSystemEntryCollection { get; }
17 | string FriendlyName { get; }
18 |
19 | int Initialise(FileBrowserEntry separator);
20 | void Focused();
21 |
22 | bool EntrySelected(ref int entryIndex);
23 | bool SongExists(SongBase song);
24 |
25 | Song LoadSongInformation(int entryIndex);
26 | void LoadSongAudio(Song song);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/TurntNinja/Game/HighScore.cs:
--------------------------------------------------------------------------------
1 | using LiteDB;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace TurntNinja.Game
9 | {
10 | public class HighScoreEntry
11 | {
12 | public ObjectId Id { get; set; }
13 | public string SongName { get; set; }
14 | public long SongID { get; set; }
15 | public List HighScores { get; set; }
16 | public DifficultyLevels Difficulty { get; set; }
17 |
18 | public HighScoreEntry()
19 | {
20 | HighScores = new List();
21 | }
22 | }
23 |
24 | public class PlayerScore
25 | {
26 | public string Name { get; set; }
27 | public long Score { get; set; }
28 | public float Accuracy { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/scripts/package-files.ps1:
--------------------------------------------------------------------------------
1 | $major = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileMajorPart
2 | $minor = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileMinorPart
3 | $build = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileBuildPart
4 | $name = 'turnt-ninja_v{0}_{1}_{2}' -f $major, $minor, $build
5 | echo $name
6 |
7 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $name .\Binaries\Release/*.dll
8 | Start-Sleep -Milliseconds 500
9 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $name .\Binaries\Release\turnt-ninja.exe
10 | Start-Sleep -Milliseconds 500
11 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $name .\Binaries\Release\turnt-ninja.exe.config
12 | Start-Sleep -Milliseconds 500
13 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $name .\Resources\
14 | Start-Sleep -Milliseconds 500
15 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $name .\Licenses\
16 | Start-Sleep -Milliseconds 500
17 | & 'C:\Program Files\WinRAR\Rar.exe' a $name '.\Turnt Ninja.lnk'
18 | Start-Sleep -Milliseconds 500
--------------------------------------------------------------------------------
/scripts/package-files-new.ps1:
--------------------------------------------------------------------------------
1 | $ENV:major = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileMajorPart
2 | $ENV:minor = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileMinorPart
3 | $ENV:build = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FileBuildPart
4 | $ENV:private = (Get-Item .\Binaries\Release\turnt-ninja.exe).VersionInfo.FilePrivatePart
5 | $ENV:name = 'turnt-ninja_v{0}_{1}_{2}' -f $ENV:major, $ENV:minor, $ENV:build
6 | echo $ENV:name
7 |
8 | & 'C:\Program Files\WinRAR\Rar.exe' a -ep -r $ENV:name .\Binaries\Release/*.dll
9 | Start-Sleep -Milliseconds 500
10 | & 'C:\Program Files\WinRAR\Rar.exe' a -ep -r $ENV:name .\Binaries\Release\turnt-ninja.exe
11 | Start-Sleep -Milliseconds 500
12 | & 'C:\Program Files\WinRAR\Rar.exe' a -ep -r $ENV:name .\Binaries\Release\turnt-ninja.exe.config
13 | Start-Sleep -Milliseconds 500
14 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $ENV:name .\Resources\
15 | Start-Sleep -Milliseconds 500
16 | & 'C:\Program Files\WinRAR\Rar.exe' a -r $ENV:name .\Licenses\
17 | Start-Sleep -Milliseconds 500
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - linux
3 | - osx
4 | language: csharp
5 | env:
6 | matrix:
7 | - CONFIGURATION=Release
8 | - CONFIGURATION=Debug
9 | global:
10 | secure: Hgz9igzcHhxN8gSxaaVqRN3YuNEeGPDoeMhv8rfZhXrfVaVXebZhiPO1A7r9ZElzNu2XlbheAPAUhSbmG5wx6N8DIypUqqRUwPyPzAB3N/zmBzh6rJtLnauzG/hmOemNApQ8VY9A89vS9+c22h+e8ULlPzd2U2D14mNC31TcoLM1foaougmLdtedWXGoS4W8WGi6tiuBIiyJxomlS6kUMhvRuLddzHGb9ctbu1NbyAk1TmGIWbVPLoOJ46ali9wdqBDsU8QGLasHZvcIMB51dld2/NxOlzWrgu6Q1GWTNW51uld/yErmuZzRg9jJebPUfWqOIOVcv9UmTH9wFPj4J2S+18uiO1o+UEPMRfDVY+apSgfepTRJ5WCZrSKseDzqUL9ObjM7bGEpTChk/fNQM2RXAO7yXevGp9GvRK/vLxQuIAhwyOG7uvT3xqMKR2ZoiXZrLhmpWu/4T3K5P09dn/m57DgZe37DRnWMEckqNbPfGsIg4l0EMx3jSFKNF/ZDMpy2DMn9Iy8FgWAGB1OysVxXy+yuDthgqUkUsGIdiQKIa/EswTWB9XwzjABTlo1NoDqDJZUXMdkvVuMCrIMZshfEA4DkI7ZaFoVMEeXADcjZ9c5oi4Xt/0XfOjxAeTfFSXcw+BInytCel37SM7/3hKbe7utuM3nNqgUA3vszNa0=
11 | script:
12 | - "./build.sh mode=$CONFIGURATION"
13 | - "./build.sh PushArtifactsAndItchBuilds mode=$CONFIGURATION"
14 | notifications:
15 | email:
16 | on_success: change
17 | on_failure: always
18 | on_start: never
19 |
--------------------------------------------------------------------------------
/docs/licenses/QuickFont.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014
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 |
--------------------------------------------------------------------------------
/docs/licenses/ColorMine.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 ColorMine.org
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
13 | all 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
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/docs/licenses/Substructio.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Patrick Yates
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 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/SongBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace TurntNinja.Audio
8 | {
9 | public class SongBase : IEquatable
10 | {
11 | public string FileSystemFriendlyName { get; set; }
12 | public string InternalName { get; set; }
13 |
14 | public string Identifier { get; set; }
15 | public string Artist { get; set; }
16 | public string TrackName { get; set; }
17 |
18 | public bool Equals(SongBase other)
19 | {
20 | return (FileSystemFriendlyName.Equals(other.FileSystemFriendlyName))
21 | && (InternalName.Equals(other.InternalName))
22 | && (Identifier.Equals(other.Identifier))
23 | && (Artist.Equals(other.Artist))
24 | && (TrackName.Equals(other.TrackName));
25 | }
26 |
27 | public override bool Equals(object obj)
28 | {
29 | SongBase s = obj as SongBase;
30 | if (s != null)
31 | return Equals(s);
32 | return false;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/TurntNinja/Settings.cs:
--------------------------------------------------------------------------------
1 | namespace TurntNinja.Properties {
2 |
3 |
4 | // This class allows you to handle specific events on the settings class:
5 | // The SettingChanging event is raised before a setting's value is changed.
6 | // The PropertyChanged event is raised after a setting's value is changed.
7 | // The SettingsLoaded event is raised after the setting values are loaded.
8 | // The SettingsSaving event is raised before the setting values are saved.
9 | internal sealed partial class Settings {
10 |
11 | public Settings() {
12 | // // To add event handlers for saving and changing settings, uncomment the lines below:
13 | //
14 | // this.SettingChanging += this.SettingChangingEventHandler;
15 | //
16 | // this.SettingsSaving += this.SettingsSavingEventHandler;
17 | //
18 | }
19 |
20 | private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
21 | // Add code to handle the SettingChangingEvent event here.
22 | }
23 |
24 | private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
25 | // Add code to handle the SettingsSaving event here.
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/Song.cs:
--------------------------------------------------------------------------------
1 | using TurntNinja.FileSystem;
2 | using CSCore;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace TurntNinja.Audio
10 | {
11 | public class Song : IDisposable
12 | {
13 | public IWaveSource SongAudio { get; set; }
14 | public bool SongAudioLoaded { get; set; } = false;
15 | public IFileSystem FileSystem { get; set; }
16 | public SongBase SongBase { get; set; }
17 |
18 | bool disposedValue = false; // To detect redundant calls
19 |
20 | public void LoadSongAudio()
21 | {
22 | FileSystem.LoadSongAudio(this);
23 | }
24 |
25 | protected virtual void Dispose(bool disposing)
26 | {
27 | if (!disposedValue)
28 | {
29 | if (disposing)
30 | {
31 | SongAudio.Dispose();
32 | }
33 | SongAudio = null;
34 |
35 | disposedValue = true;
36 | }
37 | }
38 |
39 | // This code added to correctly implement the disposable pattern.
40 | public void Dispose()
41 | {
42 | // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
43 | Dispose(true);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/TurntNinja/OpenTK.dll.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/souncloud-api-v2-notes.md:
--------------------------------------------------------------------------------
1 | # Explore API
2 |
3 | **Get explore categories**
4 |
5 | ```
6 | https://api-v2.soundcloud.com/explore/categories?limit=10&offset=0&linked_partitioning=1&client_id=ID_HERE
7 | ```
8 |
9 | Example
10 | ```
11 | https://api-v2.soundcloud.com/explore/categories?limit=10&offset=0&linked_partitioning=1&client_id=02gUJC0hH2ct1EGOcYXQIzRFU91c72Ea&app_version=d8c55ad
12 | ```
13 |
14 | **Get tracks from explore category**
15 | ```
16 | https://api-v2.soundcloud.com/explore/CATEGORY_NAME_HERE?tag=out-of-experiment&limit=10&offset=0&linked_partitioning=1&client_id=ID_HERE
17 | ```
18 |
19 | Example
20 | ```
21 | https://api-v2.soundcloud.com/explore/Hip+Hop+%26+Rap?tag=out-of-experiment&limit=10&client_id=ID_HERE
22 | ```
23 |
24 | # New Search API
25 |
26 | **Search Everything**
27 | ```
28 | https://api-v2.soundcloud.com/search?q=antidote&facet=model&user_id=698189-13257-213778-325874&limit=10&offset=0&linked_partitioning=1&client_id=02gUJC0hH2ct1EGOcYXQIzRFU91c72Ea&app_version=d8c55ad
29 | ```
30 |
31 | **Search only tracks**
32 | ```
33 | https://api-v2.soundcloud.com/search/tracks?q=antidote&facet=genre&user_id=698189-13257-213778-325874&limit=10&offset=0&linked_partitioning=1&client_id=02gUJC0hH2ct1EGOcYXQIzRFU91c72Ea&app_version=d8c55ad
34 | ```
35 |
36 | # New Charts API
37 |
38 | **Get All-Music Chart**
39 | ```
40 | https://api-v2.soundcloud.com/charts?kind=top&genre=soundcloud%3Agenres%3Aall-music&client_id=02gUJC0hH2ct1EGOcYQXIzRFU91c72Ea&limit=20&offset=0&linked_partitioning=1&app_version=1461312517
41 | ```
--------------------------------------------------------------------------------
/scripts/create-squirrel-package.ps1:
--------------------------------------------------------------------------------
1 | # create release file
2 | .\package-files-new.ps1
3 |
4 | # load release notes
5 | $rel = Get-Content .\release-notes.txt -Raw
6 |
7 | # do directory stuff
8 | $basedir = '.\SquirrelReleases\{0}_{1}_{2}_{3}' -f $ENV:major, $ENV:minor, $ENV:build, $ENV:private
9 | $dirname = '{0}\lib\net45' -f $basedir
10 | md -Force $dirname
11 | & 'C:\Program Files\WinRAR\UnRAR.exe' x -o+ $env:name $dirname
12 | pushd $basedir
13 | ..\..\.nuget\NuGet.exe spec -a '.\lib\net45\turnt-ninja.exe' -f
14 | # update spec info
15 | $xml = [xml](Get-Content 'turnt-ninja.nuspec')
16 |
17 | $xml.package.metadata.licenseUrl = "https://github.com/opcon/turnt-ninja/blob/master/LICENSE"
18 | $xml.package.metadata.projectUrl = "https://github.com/opcon/turnt-ninja"
19 | $xml.package.metadata.releaseNotes = "$rel"
20 |
21 | # delete some entries
22 | $remove = $xml.package.metadata.dependencies
23 | $xml.package.metadata.RemoveChild($remove)
24 |
25 | $remove = $xml.SelectSingleNode("//tags")
26 | $xml.package.metadata.RemoveChild($remove)
27 |
28 | $remove = $xml.SelectSingleNode("//iconUrl")
29 | $xml.package.metadata.RemoveChild($remove)
30 |
31 | # save spec file
32 | $xml.Save(("$pwd\turnt-ninja.nuspec"))
33 | ..\..\.nuget\NuGet.exe pack 'turnt-ninja.nuspec'
34 | popd
35 |
36 | # squirrel releasify
37 | $packageFile = "$basedir\turnt-ninja.$ENV:major.$ENV:minor.$ENV:build.$ENV:private.nupkg"
38 | $arguments = "--releasify $packageFile"
39 | Start-Process '.\packages\squirrel.windows.1.4.0\tools\Squirrel.exe' -ArgumentList $arguments -Wait
--------------------------------------------------------------------------------
/src/TurntNinja/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Turnt Ninja")]
9 | [assembly: AssemblyDescription("Turnt Ninja")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("VirtualHighRise")]
12 | [assembly: AssemblyProduct("")]
13 | [assembly: AssemblyCopyright("Copyright Patrick Yates © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("d4cccf09-83d4-4190-bb8f-4fca1b7451b8")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.14.*")]
36 | [assembly: AssemblyInformationalVersion("")]
37 |
--------------------------------------------------------------------------------
/paket.dependencies:
--------------------------------------------------------------------------------
1 | redirects: on
2 |
3 | source https://ci.appveyor.com/nuget/gwen-nolegacy-opentk-renderer-y8bf4l8s9nxo
4 | source https://ci.appveyor.com/nuget/onset-detection-ce96cu4ph0k5
5 | source https://ci.appveyor.com/nuget/quickfont-m6wdxb2yf1v5
6 | source https://ci.appveyor.com/nuget/cscore-lt381sf5ht3e
7 | source https://www.nuget.org/api/v2/
8 |
9 | nuget AcoustID.NET
10 | nuget CSCore.OSX = 1.1.34
11 | nuget NuGet.CommandLine
12 | nuget ColorMine >= 1.1.3.0 framework: >= net45
13 | nuget CSCore = 1.1.34 framework: >= net45
14 | nuget Gwen >= 4.0 framework: >= net45
15 | nuget Gwen.Renderer.OpenTK >= 4.0 framework: >= net45
16 | nuget DeltaCompressionDotNet 1.0.0 framework: >= net45
17 | nuget HUSL >= 1.0.1 framework: >= net45
18 | nuget LiteDB >= 1.0.4 framework: >= net45
19 | nuget MathNet.Numerics >= 3.11.1 framework: >= net45
20 | nuget Mono.Cecil 0.9.6.1 framework: >= net45
21 | nuget Newtonsoft.Json >= 8.0.3 framework: >= net45
22 | nuget OnsetDetection >= 1.0.6020.16658 framework: >= net45
23 | nuget OpenTK >= 2.0 framework: >= net45
24 | nuget QuickFont.Desktop >= 4.3.6020.17547 framework: >= net45
25 | nuget SharpFont >= 3.1.0 framework: >= net45
26 | nuget SharpFont.Dependencies >= 2.6 framework: >= net45
27 | nuget SharpRaven
28 | nuget Splat >= 1.6.2 framework: >= net45
29 | nuget squirrel.windows >= 1.4.0 framework: >= net45
30 | nuget TagLib.Portable >= 1.0.4 framework: >= net45
31 |
32 | group Deploy
33 |
34 | source https://www.nuget.org/api/v2/
35 | nuget ILRepack
36 | nuget Microsoft.Data.Services.Client = 5.6.1
37 |
38 | group Build
39 |
40 | source https://www.nuget.org/api/v2/
41 | nuget FAKE
42 |
--------------------------------------------------------------------------------
/docs/licenses/Clipper.txt:
--------------------------------------------------------------------------------
1 | The Clipper Library (including Delphi, C++ & C# source code, other accompanying
2 | code, examples and documentation), hereafter called "the Software", has been
3 | released under the following license, terms and conditions:
4 |
5 | Boost Software License - Version 1.0 - August 17th, 2003
6 | http://www.boost.org/LICENSE_1_0.txt
7 |
8 | Permission is hereby granted, free of charge, to any person or organization
9 | obtaining a copy of the Software covered by this license to use, reproduce,
10 | display, distribute, execute, and transmit the Software, and to prepare
11 | derivative works of the Software, and to permit third-parties to whom the
12 | Software is furnished to do so, all subject to the following:
13 |
14 | The copyright notices in the Software and this entire statement, including the
15 | above license grant, this restriction and the following disclaimer, must be
16 | included in all copies of the Software, in whole or in part, and all derivative
17 | works of the Software, unless such copies or derivative works are solely in the
18 | form of machine-executable object code generated by a source language processor.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
23 | THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
24 | DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 | THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/lib/Clipper/License.txt:
--------------------------------------------------------------------------------
1 | The Clipper Library (including Delphi, C++ & C# source code, other accompanying
2 | code, examples and documentation), hereafter called "the Software", has been
3 | released under the following license, terms and conditions:
4 |
5 | Boost Software License - Version 1.0 - August 17th, 2003
6 | http://www.boost.org/LICENSE_1_0.txt
7 |
8 | Permission is hereby granted, free of charge, to any person or organization
9 | obtaining a copy of the Software covered by this license to use, reproduce,
10 | display, distribute, execute, and transmit the Software, and to prepare
11 | derivative works of the Software, and to permit third-parties to whom the
12 | Software is furnished to do so, all subject to the following:
13 |
14 | The copyright notices in the Software and this entire statement, including the
15 | above license grant, this restriction and the following disclaimer, must be
16 | included in all copies of the Software, in whole or in part, and all derivative
17 | works of the Software, unless such copies or derivative works are solely in the
18 | form of machine-executable object code generated by a source language processor.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
23 | THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY
24 | DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING
25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 | THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/src/TurntNinja/Core/HUSLColor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections;
4 | using OpenTK.Graphics;
5 |
6 | namespace TurntNinja.Core
7 | {
8 | public struct HUSLColor
9 | {
10 | public double H;
11 | public double S;
12 | public double L;
13 |
14 | public HUSLColor (double h, double s, double l)
15 | {
16 | H = h;
17 | S = s;
18 | L = l;
19 | }
20 |
21 | public HUSLColor(Color4 color)
22 | {
23 | //var c = new ColorMine.ColorSpaces.Rgb { R = color.R*255, G = color.G*255, B = color.B*255 };
24 | //var r = c.To();
25 | //H = r.H;
26 | //S = r.S;
27 | //L = r.L;
28 | var res = HUSL.ColorConverter.RGBToHUSL(new List { color.R, color.G, color.B });
29 | H = res[0];
30 | S = res[1];
31 | L = res[2];
32 | }
33 |
34 | public static HUSLColor FromColor4(Color4 color)
35 | {
36 | return new HUSLColor(color);
37 | }
38 |
39 | public static Color4 ToColor4(HUSLColor color)
40 | {
41 | //var c = new ColorMine.ColorSpaces.Hsl { H = color.H, L = color.L, S = color.S };
42 | //var r = c.ToRgb();
43 | //return new Color4((float)r.R/255, (float)r.G/255, (float)r.B/255, 1.0f);
44 | var res = HUSL.ColorConverter.HUSLToRGB(new List{ color.H, color.S, color.L });
45 | return new Color4((byte)((res[0]) * 255), (byte)((res[1]) * 255), (byte)((res[2]) * 255), 255);
46 | }
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/turnt-ninja.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{9281E6FF-BFC1-45FA-9EE5-2E50D35E0D58}"
7 | ProjectSection(SolutionItems) = preProject
8 | paket.dependencies = paket.dependencies
9 | EndProjectSection
10 | EndProject
11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TurntNinja", "src\TurntNinja\TurntNinja.csproj", "{630022B3-A336-42AA-85D5-5AACE71FDE6C}"
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Substructio", "..\Substructio\src\Substructio.csproj", "{5B3F1066-88DA-4F9C-8DFE-646C40B412C2}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BB0E8FBB-6D2E-4F49-A59E-F90B598AA4E6}"
16 | ProjectSection(SolutionItems) = preProject
17 | build.fsx = build.fsx
18 | EndProjectSection
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {630022B3-A336-42AA-85D5-5AACE71FDE6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {630022B3-A336-42AA-85D5-5AACE71FDE6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {630022B3-A336-42AA-85D5-5AACE71FDE6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {630022B3-A336-42AA-85D5-5AACE71FDE6C}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {5B3F1066-88DA-4F9C-8DFE-646C40B412C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {5B3F1066-88DA-4F9C-8DFE-646C40B412C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {5B3F1066-88DA-4F9C-8DFE-646C40B412C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {5B3F1066-88DA-4F9C-8DFE-646C40B412C2}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | EndGlobal
39 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/AcoustIDCSCore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using AcoustID.Chromaprint;
7 | using CSCore;
8 |
9 | namespace TurntNinja.Audio
10 | {
11 | class AcoustIDCSCore : AcoustID.Audio.IDecoder, IDisposable
12 | {
13 | private readonly CSCore.IWaveSource _waveSource;
14 | private const int BUFFER_SIZE = 2 * 192000;
15 |
16 | private byte[] _buffer;
17 | private short[] _data;
18 |
19 | public AcoustIDCSCore(CSCore.IWaveSource waveSource)
20 | {
21 | _waveSource = waveSource;
22 |
23 | if (_waveSource.WaveFormat.BitsPerSample != 16)
24 | throw new ArgumentOutOfRangeException(nameof(waveSource), "Expected 16 bit audio");
25 | }
26 |
27 | public int Channels { get { return _waveSource.WaveFormat.Channels; } }
28 |
29 | public int SampleRate { get { return _waveSource.WaveFormat.SampleRate; } }
30 |
31 | public bool Decode(IAudioConsumer consumer, int maxLength)
32 | {
33 | if (_waveSource == null) return false;
34 | if (_buffer == null) _buffer = new byte[BUFFER_SIZE * 2];
35 | if (_data == null) _data = new short[BUFFER_SIZE];
36 |
37 | // maxLength is in seconds of audio
38 | // Calculate maximum bytes to read
39 | var maxBytes = maxLength * Channels * SampleRate;
40 |
41 | // Calculate actual bytes we can fit in buffer
42 | var bytesToRead = Math.Min(maxBytes, _buffer.Length);
43 |
44 | int read = 0;
45 |
46 | while ((read = _waveSource.Read(_buffer, 0, bytesToRead)) > 0)
47 | {
48 | Buffer.BlockCopy(_buffer, 0, _data, 0, read);
49 |
50 | consumer.Consume(_data, read / 2);
51 |
52 | maxBytes -= read / 2;
53 | if (maxBytes <= 0)
54 | break;
55 | bytesToRead = Math.Min(maxBytes, _buffer.Length);
56 | }
57 |
58 | return true;
59 | }
60 |
61 | public void Dispose()
62 | {
63 | _waveSource.Dispose();
64 | _buffer = null;
65 | _data = null;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/docs/licenses/OpenTK.txt:
--------------------------------------------------------------------------------
1 | The Open Toolkit library license
2 | Copyright (c) 2006 - 2014 Stefanos Apostolopoulos (stapostol@gmail.com) for the Open Toolkit library.
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6 | Third parties
7 | OpenTK.Platform.Windows and OpenTK.Platform.X11 include portions of the Mono class library. These portions are covered by the following license:
8 | Copyright (c) 2004 Novell, Inc.
9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/paket.lock:
--------------------------------------------------------------------------------
1 | REDIRECTS: ON
2 | NUGET
3 | remote: https://www.nuget.org/api/v2
4 | AcoustID.NET (1.3.2.1)
5 | ColorMine (1.1.3) - framework: >= net45
6 | DeltaCompressionDotNet (1.0) - framework: >= net45
7 | HUSL (1.0.1) - framework: >= net45
8 | LiteDB (3.0.1) - framework: >= net45
9 | MathNet.Numerics (3.17) - framework: >= net45
10 | Mono.Cecil (0.9.6.1) - framework: >= net45
11 | Newtonsoft.Json (9.0.1)
12 | NuGet.CommandLine (3.5)
13 | OpenTK (2.0) - framework: >= net45
14 | SharpFont (4.0.1) - framework: >= net45
15 | SharpFont.Dependencies
16 | SharpRaven (2.2)
17 | Newtonsoft.Json (>= 6.0.8)
18 | Splat (2.0) - framework: >= net45
19 | squirrel.windows (1.5.2) - framework: >= net45
20 | DeltaCompressionDotNet (>= 1.0 < 2.0)
21 | Mono.Cecil (>= 0.9.6.1)
22 | Splat (>= 1.6.2)
23 | TagLib.Portable (1.0.4) - framework: >= net45
24 | remote: https://ci.appveyor.com/nuget/cscore-lt381sf5ht3e
25 | CSCore (1.1.34)
26 | CSCore.OSX (1.1.34)
27 | CSCore (>= 1.1.34)
28 | remote: https://ci.appveyor.com/nuget/gwen-nolegacy-opentk-renderer-y8bf4l8s9nxo
29 | Gwen (4.0.6207.13461) - framework: >= net45
30 | OpenTK (>= 2.0)
31 | Gwen.Renderer.OpenTK (4.0.6207.13461) - framework: >= net45
32 | Gwen (>= 4.0)
33 | OpenTK (>= 2.0)
34 | QuickFont.Desktop (>= 4.3.6018.34176)
35 | SharpFont (>= 3.1)
36 | SharpFont.Dependencies (>= 2.6)
37 | remote: https://ci.appveyor.com/nuget/onset-detection-ce96cu4ph0k5
38 | OnsetDetection (1.0.6071.26192) - framework: >= net45
39 | CSCore (>= 1.1.15)
40 | MathNet.Numerics (>= 3.11.1)
41 | remote: https://ci.appveyor.com/nuget/quickfont-m6wdxb2yf1v5
42 | QuickFont.Desktop (4.4.6239.14169) - framework: >= net45
43 | OpenTK (>= 2.0)
44 | SharpFont (>= 4.0)
45 | SharpFont.Dependencies (>= 2.6)
46 | SharpFont.Dependencies (2.6) - framework: >= net45
47 |
48 | GROUP Build
49 | NUGET
50 | remote: https://www.nuget.org/api/v2
51 | FAKE (4.50.1)
52 |
53 | GROUP Deploy
54 | NUGET
55 | remote: https://www.nuget.org/api/v2
56 | ILRepack (2.0.12)
57 | Microsoft.Data.Edm (5.6.1)
58 | Microsoft.Data.OData (5.6.1)
59 | Microsoft.Data.Edm (5.6.1)
60 | System.Spatial (5.6.1)
61 | Microsoft.Data.Services.Client (5.6.1)
62 | Microsoft.Data.OData (5.6.1)
63 | System.Spatial (5.6.1)
64 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/docs/licenses/CSCore.md:
--------------------------------------------------------------------------------
1 | ## Microsoft Public License (Ms-PL) ##
2 |
3 | Microsoft Public License (Ms-PL)
4 |
5 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
6 |
7 | #### 1. Definitions ####
8 |
9 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
10 |
11 | A "contribution" is the original software, or any additions or changes to the software.
12 |
13 | A "contributor" is any person that distributes its contribution under this license.
14 |
15 | "Licensed patents" are a contributor's patent claims that read directly on its contribution.
16 |
17 | #### 2. Grant of Rights ####
18 |
19 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
20 |
21 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
22 |
23 | #### 3. Conditions and Limitations ####
24 |
25 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
26 |
27 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
28 |
29 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
30 |
31 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
32 |
33 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
34 |
--------------------------------------------------------------------------------
/scripts/Butler.fs:
--------------------------------------------------------------------------------
1 | module Fake.Butler
2 |
3 | open System.Net
4 |
5 | let butler_linux_32 = "https://dl.itch.ovh/butler/linux-386/head/butler"
6 | let butler_linux_64 = "https://dl.itch.ovh/butler/linux-amd64/head/butler"
7 | let butler_mac = "https://dl.itch.ovh/butler/darwin-amd64/head/butler"
8 | let butler_windows_32 = "https://dl.itch.ovh/butler/windows-386/head/butler.exe"
9 | let butler_windows_64 = "https://dl.itch.ovh/butler/windows-amd64/head/butler.exe"
10 |
11 | let butlerFileName =
12 | match EnvironmentHelper.isWindows with
13 | | true -> "butler.exe"
14 | | false -> "butler"
15 |
16 | let butlerURL =
17 | if EnvironmentHelper.isLinux then match System.Environment.Is64BitOperatingSystem with
18 | | true -> butler_linux_64
19 | | false -> butler_linux_32
20 | else if EnvironmentHelper.isMacOS then butler_mac
21 | else if EnvironmentHelper.isWindows then match System.Environment.Is64BitOperatingSystem with
22 | | true -> butler_windows_64
23 | | false -> butler_windows_32
24 | else butler_windows_32
25 |
26 | let DownloadButler (butlerFolder:string) =
27 | let wc = new WebClient()
28 | try
29 | wc.DownloadFile(butlerURL, butlerFolder + butlerFileName)
30 | with
31 | | _ -> (let SSLUri = new System.Uri(butlerURL)
32 | let uriBuilder = new System.UriBuilder(butlerURL)
33 | uriBuilder.Scheme <- System.Uri.UriSchemeHttp
34 | uriBuilder.Port <- -1
35 | let nonSSLURL = uriBuilder.ToString()
36 | trace ("Download failed, trying non-ssl url " + nonSSLURL)
37 | wc.DownloadFile(nonSSLURL, butlerFolder + butlerFileName))
38 | match EnvironmentHelper.isWindows with
39 | | false -> (let result = ProcessHelper.ExecProcess (fun info ->
40 | info.FileName <- "chmod"
41 | info.Arguments <- "u+x " + butlerFolder + butlerFileName) (System.TimeSpan.FromSeconds 10.0)
42 | if result <> 0 then failwithf "Couldn't set executable permissions on bulter")
43 | | _ -> ()
44 | trace ("Butler downloaded to " + (butlerFolder + butlerFileName))
45 |
46 | let PushBuild (butlerFolder:string) (buildFileOrDir:string) (target:string) (channel:string) (version:string) (fixPermissions:bool) =
47 | let butlerFullPath = butlerFolder + butlerFileName
48 |
49 | let flags =
50 | "" + (if fixPermissions then "--fix-permissions " else "")
51 | + ("--userversion=" + version + " ")
52 |
53 | ProcessHelper.redirectOutputToTrace = true |> ignore
54 | let result = ProcessHelper.ExecProcess (fun info ->
55 | info.FileName <- butlerFullPath
56 | info.Arguments <- (sprintf "push %s%s %s:%s" flags buildFileOrDir target channel)) (System.TimeSpan.FromMinutes 5.0)
57 | if result <> 0 then failwithf "Butler push failed"
58 | result
--------------------------------------------------------------------------------
/src/TurntNinja/Core/Settings/PropertySettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Configuration;
5 | using System.Linq;
6 | using System.Threading;
7 | using Gwen.Control;
8 | using Substructio.Core.Settings;
9 |
10 | namespace TurntNinja.Core.Settings
11 | {
12 | class PropertySettings : IGameSettings
13 | {
14 | private bool _loaded;
15 | private Dictionary _settings;
16 |
17 | public object this[string key]
18 | {
19 | get
20 | {
21 | if (!_loaded) throw new Exception("GameSettings was not loaded before access");
22 | if (_settings.ContainsKey(key)) return _settings[key].PropertyValue;
23 | throw new GameSettingNotFoundException("The game setting with key {0} was not found", key);
24 | }
25 | set { if (_settings.ContainsKey(key)) _settings[key].PropertyValue = value; }
26 | }
27 |
28 | public void Save()
29 | {
30 | Properties.Settings.Default.Save();
31 | }
32 |
33 | public void Load()
34 | {
35 | if (_loaded) throw new Exception("Game settings already loaded, can't load again");
36 | if (Properties.Settings.Default.UpgradeRequired)
37 | {
38 | Properties.Settings.Default.Upgrade();
39 | Properties.Settings.Default.UpgradeRequired = false;
40 | Properties.Settings.Default.Save();
41 | }
42 |
43 | //Properties.Settings.Default.Reload();
44 | //load all the settings
45 | _settings = new Dictionary();
46 | SettingsPropertyValue[] values = new SettingsPropertyValue[Properties.Settings.Default.PropertyValues.Count];
47 | Properties.Settings.Default.PropertyValues.CopyTo(values, 0);
48 | foreach (SettingsPropertyValue propertyValue in values)
49 | {
50 | _settings.Add(propertyValue.Name, propertyValue);
51 | }
52 | _loaded = true;
53 | }
54 |
55 | public Dictionary GetAllSettings()
56 | {
57 | var ret = new Dictionary();
58 | foreach (var kvp in _settings)
59 | {
60 | ret.Add(kvp.Key, kvp.Value.PropertyValue);
61 | }
62 | return ret;
63 | }
64 | }
65 |
66 | public class GameSettingNotFoundException : Exception
67 | {
68 | public GameSettingNotFoundException() : base()
69 | {
70 | }
71 |
72 | public GameSettingNotFoundException(string message) : base(message)
73 | {
74 | }
75 |
76 | public GameSettingNotFoundException(string message, Exception innerException) : base(message, innerException)
77 | {
78 | }
79 |
80 | public GameSettingNotFoundException(string format, params object[] args) : base(string.Format(format, args))
81 | {
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/TurntNinja/FileSystem/RecentFileSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using TurntNinja.Audio;
8 | using System.IO;
9 |
10 | namespace TurntNinja.FileSystem
11 | {
12 | class RecentFileSystem : IFileSystem
13 | {
14 | List _recentSongs;
15 | List _recentSongList;
16 | List _recentSongBaseList;
17 |
18 | public ReadOnlyCollection FileSystemEntryCollection { get { return _recentSongs.AsReadOnly(); } }
19 |
20 | public string FriendlyName { get { return "Recent Songs"; } }
21 |
22 | public List FileSystemCollection { get; set; }
23 |
24 | public RecentFileSystem(List RecentSongList)
25 | {
26 | _recentSongBaseList = RecentSongList;
27 | _recentSongList = new List();
28 | _recentSongs = new List();
29 | }
30 |
31 | public bool EntrySelected(ref int entryIndex)
32 | {
33 | //return true if we've found a song
34 | if (_recentSongs[entryIndex].EntryType.HasFlag(FileBrowserEntryType.Song)) return true;
35 |
36 | //something has gone wrong
37 | return false;
38 | }
39 |
40 | public int Initialise(FileBrowserEntry separator)
41 | {
42 | BuildRecentSongList();
43 | return 0;
44 | }
45 |
46 | private void BuildRecentSongList()
47 | {
48 | //Build song list
49 | var _toRemove = new List();
50 | _recentSongList.Clear();
51 | foreach (var s in _recentSongBaseList)
52 | {
53 | var fs = FileSystemCollection.FirstOrDefault(f => f.FriendlyName.Equals(s.FileSystemFriendlyName, StringComparison.OrdinalIgnoreCase));
54 | if (fs != null && fs.SongExists(s))
55 | _recentSongList.Add(new Song { SongBase = s, FileSystem = fs });
56 | else
57 | _toRemove.Add(s);
58 |
59 | }
60 | _toRemove.ForEach(s => _recentSongBaseList.Remove(s));
61 | _recentSongs.Clear();
62 |
63 | _recentSongs = _recentSongList.ConvertAll(s => new FileBrowserEntry { EntryType = FileBrowserEntryType.Song, Name = s.SongBase.Identifier, Path = s.SongBase.InternalName });
64 | }
65 |
66 | public void LoadSongAudio(Song song)
67 | {
68 | // Sanity checks
69 | if (!File.Exists(song.SongBase.InternalName)) throw new Exception("File not found: " + song.SongBase.InternalName);
70 |
71 | song.SongAudio = CSCore.Codecs.CodecFactory.Instance.GetCodec(song.SongBase.InternalName);
72 | song.SongAudioLoaded = true;
73 | }
74 |
75 | public Song LoadSongInformation(int entryIndex)
76 | {
77 | return _recentSongList[entryIndex];
78 | }
79 |
80 | public bool SongExists(SongBase song)
81 | {
82 | throw new NotImplementedException();
83 | }
84 |
85 | public void Focused()
86 | {
87 | BuildRecentSongList();
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/TurntNinja/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 |
55 | true
56 |
57 |
58 |
59 |
60 |
61 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/.paket/paket.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 |
7 | true
8 | $(MSBuildThisFileDirectory)
9 | $(MSBuildThisFileDirectory)..\
10 | /Library/Frameworks/Mono.framework/Commands/mono
11 | mono
12 |
13 |
14 |
15 | $(PaketToolsPath)paket.exe
16 | $(PaketToolsPath)paket.bootstrapper.exe
17 | "$(PaketExePath)"
18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
19 | "$(PaketBootStrapperExePath)" $(PaketBootStrapperCommandArgs)
20 | $(MonoPath) --runtime=v4.0.30319 $(PaketBootStrapperExePath) $(PaketBootStrapperCommandArgs)
21 |
22 | $(MSBuildProjectDirectory)\paket.references
23 | $(MSBuildStartupDirectory)\paket.references
24 | $(MSBuildProjectFullPath).paket.references
25 | $(PaketCommand) restore --references-files "$(PaketReferences)"
26 | $(PaketBootStrapperCommand)
27 |
28 | RestorePackages; $(BuildDependsOn);
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | [Bb]in/
16 | [Oo]bj/
17 | [Bb]inaries/
18 |
19 | # OpenTK Debug Binaries
20 | OpenTK Debug/
21 |
22 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
23 | !packages/*/build/
24 |
25 | # MSTest test Results
26 | [Tt]est[Rr]esult*/
27 | [Bb]uild[Ll]og.*
28 |
29 | *_i.c
30 | *_p.c
31 | *.ilk
32 | *.meta
33 | *.obj
34 | *.pch
35 | *.pdb
36 | *.pgc
37 | *.pgd
38 | *.rsp
39 | *.sbr
40 | *.tlb
41 | *.tli
42 | *.tlh
43 | *.tmp
44 | *.tmp_proj
45 | *.log
46 | *.vspscc
47 | *.vssscc
48 | .builds
49 | *.pidb
50 | *.log
51 | *.scc
52 |
53 | # Visual C++ cache files
54 | ipch/
55 | *.aps
56 | *.ncb
57 | *.opensdf
58 | *.sdf
59 | *.cachefile
60 |
61 | # Visual Studio profiler
62 | *.psess
63 | *.vsp
64 | *.vspx
65 |
66 | # Guidance Automation Toolkit
67 | *.gpState
68 |
69 | # ReSharper is a .NET coding add-in
70 | _ReSharper*/
71 | *.[Rr]e[Ss]harper
72 |
73 | # TeamCity is a build add-in
74 | _TeamCity*
75 |
76 | # DotCover is a Code Coverage Tool
77 | *.dotCover
78 |
79 | # NCrunch
80 | *.ncrunch*
81 | .*crunch*.local.xml
82 |
83 | # Installshield output folder
84 | [Ee]xpress/
85 |
86 | # DocProject is a documentation generator add-in
87 | DocProject/buildhelp/
88 | DocProject/Help/*.HxT
89 | DocProject/Help/*.HxC
90 | DocProject/Help/*.hhc
91 | DocProject/Help/*.hhk
92 | DocProject/Help/*.hhp
93 | DocProject/Help/Html2
94 | DocProject/Help/html
95 |
96 | # Click-Once directory
97 | publish/
98 |
99 | # Publish Web Output
100 | *.Publish.xml
101 |
102 | # NuGet Packages Directory
103 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
104 | packages/
105 |
106 | # Windows Azure Build Output
107 | csx
108 | *.build.csdef
109 |
110 | # Windows Store app package directory
111 | AppPackages/
112 |
113 | # Others
114 | sql/
115 | *.Cache
116 | ClientBin/
117 | [Ss]tyle[Cc]op.*
118 | ~$*
119 | *~
120 | *.dbmdl
121 | *.[Pp]ublish.xml
122 | *.pfx
123 | *.publishsettings
124 |
125 | # RIA/Silverlight projects
126 | Generated_Code/
127 |
128 | # Backup & report files from converting an old project file to a newer
129 | # Visual Studio version. Backup files are not needed, because we have git ;-)
130 | _UpgradeReport_Files/
131 | Backup*/
132 | UpgradeLog*.XML
133 | UpgradeLog*.htm
134 |
135 | # SQL Server files
136 | App_Data/*.mdf
137 | App_Data/*.ldf
138 |
139 |
140 | #LightSwitch generated files
141 | GeneratedArtifacts/
142 | _Pvt_Extensions/
143 | ModelManifest.xml
144 |
145 | #Performance profiling files
146 | Performance Profiling/
147 | *.nvuser
148 |
149 | #MonoDevelop
150 | *.userprefs
151 |
152 | # =========================
153 | # Windows detritus
154 | # =========================
155 |
156 | # Windows image file caches
157 | Thumbs.db
158 | ehthumbs.db
159 |
160 | # Folder config file
161 | Desktop.ini
162 |
163 | # Recycle Bin used on file shares
164 | $RECYCLE.BIN/
165 |
166 | # Mac desktop service store files
167 | .DS_Store
168 | /BeatDetection.exe.config
169 | /BeatDetection.sln.DotSettings
170 | /BeatDetection.vshost.exe.config
171 | /Substructio/packages.config
172 | /CrashLogs
173 |
174 | # Sublime Text Stuff
175 | *.sublime*
176 |
177 | *.csv
178 |
179 | # Squirrel and autobuild/release stuff
180 | Releases/
181 | SquirrelReleases/
182 | *.rar
183 |
184 | # FAKE
185 | .fake/
186 |
187 | # Build artifacts
188 | tmp/
189 |
190 | # Deploy
191 | deploy/
192 |
193 | # Paket
194 | paket.exe
195 |
196 | # vim
197 | *.swp
198 |
199 | # kickstart files
200 | kickstart.zip
201 |
202 | # butler
203 | butler
204 | butler.exe
--------------------------------------------------------------------------------
/src/TurntNinja/Logging/SentryErrorReporting.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Substructio.Logging;
7 | using SharpRaven;
8 | using SharpRaven.Data;
9 | using Substructio.Core;
10 |
11 | namespace TurntNinja.Logging
12 | {
13 | class SentryErrorReporting : IErrorReporting
14 | {
15 | readonly Dsn _sentryDsn;
16 | readonly string _userGUID;
17 |
18 | RavenClient _sentryClient;
19 |
20 | public SentryErrorReporting(string sentryURL, string environment, string version, string userGUID, Platform platform, string platformVersion, bool scrubUserName = false,
21 | Dictionary extraTags = null)
22 | {
23 | _sentryDsn = new Dsn(sentryURL);
24 | _userGUID = userGUID;
25 | InitialiseSentryClient();
26 |
27 | _sentryClient.Environment = environment;
28 | _sentryClient.Release = version;
29 | _sentryClient.Tags["OS"] = $"{platform} {platformVersion}";
30 | if (extraTags != null)
31 | AddTags(extraTags);
32 |
33 | if (scrubUserName) _sentryClient.LogScrubber = new SentryUserScrubber();
34 | }
35 |
36 | private void InitialiseSentryClient()
37 | {
38 | _sentryClient = new RavenClient(_sentryDsn, new CustomJsonPacketFactory(), null, new SentryUserGUIDFactory(_userGUID));
39 | }
40 |
41 | public void AddTags(IDictionary tags)
42 | {
43 | foreach (var t in tags)
44 | if (!_sentryClient.Tags.ContainsKey(t.Key)) _sentryClient.Tags.Add(t);
45 | }
46 |
47 | public string ReportError(Exception ex)
48 | {
49 | return ReportErrorAsync(ex).Result;
50 | }
51 |
52 | public string ReportMessage(string message)
53 | {
54 | return ReportMessageAsync(message).Result;
55 | }
56 |
57 | public Task ReportErrorAsync(Exception ex)
58 | {
59 | var sentryEvent = new SentryEvent(ex);
60 | return _sentryClient.CaptureAsync(sentryEvent);
61 | }
62 |
63 | public Task ReportMessageAsync(string message)
64 | {
65 | var sentryMessage = new SentryMessage(message);
66 | var sentryEvent = new SentryEvent(sentryMessage);
67 | return _sentryClient.CaptureAsync(sentryEvent);
68 | }
69 | }
70 |
71 | class CustomJsonPacketFactory : JsonPacketFactory
72 | {
73 | protected override JsonPacket OnCreate(JsonPacket jsonPacket)
74 | {
75 | // Scrub servername from the json packet since we don't need it
76 | jsonPacket.ServerName = "";
77 | return jsonPacket;
78 | }
79 | }
80 |
81 | class SentryUserGUIDFactory : SentryUserFactory
82 | {
83 | readonly string _username;
84 |
85 | public SentryUserGUIDFactory(string username)
86 | {
87 | _username = username;
88 | }
89 | protected override SentryUser OnCreate(SentryUser user)
90 | {
91 | return new SentryUser(_username);
92 | }
93 | }
94 |
95 | class SentryUserScrubber : SharpRaven.Logging.IScrubber
96 | {
97 | public string Scrub(string input)
98 | {
99 | var search = "user\":{";
100 | var start = input.IndexOf(search, StringComparison.Ordinal) + search.Length;
101 | var end = input.IndexOf("}", start, StringComparison.Ordinal);
102 | var ret = input.Substring(0, start) + input.Substring(end, input.Length - end);
103 | return ret;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/TurntNinja/Core/Settings/JsonSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Substructio.Core.Settings;
7 | using Newtonsoft.Json;
8 | using Newtonsoft.Json.Linq;
9 | using System.IO;
10 |
11 | namespace TurntNinja.Core.Settings
12 | {
13 | class JsonSettings : IGameSettings
14 | {
15 | string _settingsFile;
16 | PropertySettings _propertySettings;
17 | Dictionary _defaultSettings;
18 | Dictionary _settings;
19 | bool _loaded;
20 |
21 | public JsonSettings(PropertySettings ps, string settingsFile)
22 | {
23 | _propertySettings = ps;
24 | _settingsFile = settingsFile;
25 | }
26 | public object this[string key]
27 | {
28 | get
29 | {
30 | if (!_loaded) throw new Exception("GameSettings was not loaded before access");
31 | if (_settings.ContainsKey(key)) return _settings[key];
32 | throw new GameSettingNotFoundException("The game setting with key {0} was not found", key);
33 | }
34 | set
35 | {
36 | if (_settings.ContainsKey(key)) _settings[key] = value;
37 | }
38 | }
39 |
40 | public void Load()
41 | {
42 | _defaultSettings = _propertySettings.GetAllSettings();
43 | _settings = new Dictionary();
44 |
45 | foreach (var kvp in _defaultSettings)
46 | {
47 | _settings.Add(kvp.Key, kvp.Value);
48 | }
49 |
50 | if (File.Exists(_settingsFile))
51 | {
52 | var jsonSettings = JArray.Parse(File.ReadAllText(_settingsFile));
53 | foreach (var jobj in jsonSettings)
54 | {
55 | var value = jobj["Value"].ToObject();
56 | var name = jobj["Name"].ToObject();
57 | var type = jobj["Type"].ToObject();
58 |
59 | var valType = Type.GetType(type);
60 | var setting = JsonConvert.DeserializeObject(value, valType);
61 |
62 | // If this setting doesn't exist anymore, skip it
63 | if (!_settings.ContainsKey(name)) continue;
64 |
65 | _settings[name] = setting;
66 | }
67 | }
68 | _loaded = true;
69 | }
70 |
71 | public void Save()
72 | {
73 | var jsonSettings = new JArray();
74 | foreach (var kvp in _settings)
75 | {
76 | // Skip this setting if it has not changed
77 | if (kvp.Value == _defaultSettings[kvp.Key]) continue;
78 |
79 | // Otherwise setting has changed, save it
80 | var seralized = JsonConvert.SerializeObject(kvp.Value);
81 | var jobj = new JObject();
82 | jobj.Add("Value", seralized);
83 | jobj.Add(new JProperty("Name", kvp.Key));
84 | jobj.Add(new JProperty("Type", kvp.Value.GetType().AssemblyQualifiedName));
85 | jsonSettings.Add(jobj);
86 | }
87 | using (StreamWriter file = File.CreateText(_settingsFile))
88 | using (JsonTextWriter writer = new JsonTextWriter(file))
89 | {
90 | writer.Formatting = Formatting.Indented;
91 | jsonSettings.WriteTo(writer);
92 | }
93 | }
94 |
95 | public Dictionary GetAllSettings()
96 | {
97 | return new Dictionary(_settings);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/TurntNinja/Game/Difficulty.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using OpenTK;
7 |
8 | namespace TurntNinja.Game
9 | {
10 | public class DifficultyOptions
11 | {
12 | public float Speed { get; private set; }
13 | public float VeryCloseDistance{ get; private set; }
14 | public float CloseDistance { get; private set; }
15 | public float RotationSpeed { get; private set; }
16 | public float BeatSkipDistance { get; private set; }
17 | public float DifficultyMultiplier { get; private set; }
18 |
19 | private const float MinSpeed = 400f;
20 | private const float MaxSpeed = 2000f;
21 | private const float SpeedMultiplier = 0.0025f*4;
22 |
23 | private const float MinVeryCloseDistance = 0.05f;
24 | private const float MaxVeryCloseDistance = 0.4f;
25 | private const float VeryCloseDistanceMultiplier = 0.4f*0.5f;
26 |
27 | private const float MinCloseDistance = 0.2f;
28 | private const float MaxCloseDistance = 0.6f;
29 | private const float CloseDistanceMultiplier = 0.6f*0.5f;
30 |
31 | private const float MinRotationSpeed = 0.0f;
32 | private const float MaxRotationSpeed = 4.0f;
33 | private const float RotationSpeedMultiplier = 0.5f*3;
34 |
35 | private const float MinBeatSkipDistance = 0.0f;
36 | private const float MaxBeatSkipDistance = 1.0f;
37 |
38 | public static DifficultyOptions Easy
39 | {
40 | get
41 | {
42 | return new DifficultyOptions(500f, 0.32f, 0.5f, 1.0f, 0.25f, 3.0f);
43 | }
44 | }
45 |
46 | public static DifficultyOptions Medium
47 | {
48 | get
49 | {
50 | return new DifficultyOptions(550f, 0.2f, 0.4f, 1.2f, 0.15f, 6.0f);
51 | }
52 | }
53 |
54 | public static DifficultyOptions Hard
55 | {
56 | get
57 | {
58 | return new DifficultyOptions(600f, 0.15f, 0.35f, 1.5f, 0.125f, 9.0f);
59 | }
60 | }
61 |
62 | public static DifficultyOptions Ultra
63 | {
64 | get
65 | {
66 | return new DifficultyOptions(700f, 0.15f, 0.30f, 1.9f, 0.0f, 12.0f);
67 | }
68 | }
69 |
70 | public static DifficultyOptions Ninja
71 | {
72 | get
73 | {
74 | return new DifficultyOptions(800f, 0.15f, 0.25f, 2.2f, 0.0f, 15.0f);
75 | }
76 | }
77 |
78 | private DifficultyOptions(float speed, float vCloseDistance, float closeDistance, float rotationSpeed, float beatSkipDistance, float multiplier)
79 | {
80 | Speed = MathHelper.Clamp(speed, MinSpeed, MaxSpeed);
81 | VeryCloseDistance = MathHelper.Clamp(vCloseDistance, MinVeryCloseDistance, MaxVeryCloseDistance);
82 | CloseDistance = MathHelper.Clamp(closeDistance, MinCloseDistance, MaxCloseDistance);
83 | RotationSpeed = MathHelper.Clamp(rotationSpeed, MinRotationSpeed, MaxRotationSpeed);
84 | BeatSkipDistance = MathHelper.Clamp(beatSkipDistance, MinBeatSkipDistance, MaxBeatSkipDistance);
85 | DifficultyMultiplier = multiplier;
86 | }
87 |
88 | public float GetScoreMultiplier()
89 | {
90 | return DifficultyMultiplier;
91 | //return (SpeedMultiplier*Speed)
92 | // + (VeryCloseDistanceMultiplier*(1/VeryCloseDistance))
93 | // + (CloseDistanceMultiplier*(1/CloseDistance))
94 | // + (RotationSpeedMultiplier*RotationSpeed);
95 | }
96 | }
97 |
98 | public enum DifficultyLevels
99 | {
100 | Easy,
101 | Medium,
102 | Hard,
103 | Ultra,
104 | Ninja
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/TurntNinja/Game/OnsetCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using TurntNinja.Core;
7 |
8 | namespace TurntNinja.Game
9 | {
10 | class OnsetCollection
11 | {
12 | ///
13 | /// Collection of onset times
14 | ///
15 | public float[] OnsetTimes { get; private set; }
16 |
17 | ///
18 | /// Collection of beat frequencies
19 | ///
20 | public float[] BeatFrequencies { get; private set; }
21 |
22 | public float MaxBeatFrequency;
23 | public float MinBeatFrequency;
24 |
25 | ///
26 | /// Number of onsets
27 | ///
28 | public readonly int Count;
29 |
30 | ///
31 | /// Collection of pulse data
32 | ///
33 | public PulseData[] PulseDataCollection { get; private set; }
34 |
35 | ///
36 | /// How many onsets have been reached this frame
37 | ///
38 | public int OnsetsReached { get; private set; }
39 |
40 | ///
41 | /// Whether we need to start pulsing because we are near an onset
42 | ///
43 | public bool BeginPulsing { get; private set; }
44 |
45 | ///
46 | /// The index of the current onset
47 | ///
48 | public int OnsetIndex { get; private set; } = 0;
49 |
50 | ///
51 | /// Whether we are pulsing at the moment
52 | ///
53 | public bool Pulsing { get; private set; }
54 |
55 | ///
56 | /// The total elapsed game time for this
57 | ///
58 | public double ElapsedGameTime { get; private set; }
59 |
60 | private double _onsetTimeBuffer = 0.0f;
61 |
62 | public OnsetCollection(int onsetCount)
63 | {
64 | Count = onsetCount;
65 | OnsetTimes = new float[Count];
66 | PulseDataCollection = new PulseData[Count];
67 | }
68 |
69 | public void AddOnsets(float[] onsetTimes, float[] beatFrequencies)
70 | {
71 | OnsetTimes = onsetTimes;
72 | BeatFrequencies = beatFrequencies;
73 | MaxBeatFrequency = BeatFrequencies.Max();
74 | MinBeatFrequency = BeatFrequencies.Min();
75 | for (int i = 0; i < Count; i++)
76 | {
77 | PulseDataCollection[i] = new PulseData {
78 | PulseDirection = 1,
79 | PulseMultiplier = Math.Pow(BeatFrequencies[i] * 60, 1) + 70,
80 | PulseWidth = 0,
81 | PulseWidthMax = 25,
82 | Pulsing = false };
83 | }
84 | }
85 |
86 | public void Update(double time)
87 | {
88 | ElapsedGameTime += time;
89 |
90 | OnsetIndex += OnsetsReached;
91 | if (BeginPulsing)
92 | {
93 | BeginPulsing = false;
94 | Pulsing = true;
95 | }
96 | if (OnsetsReached > 0)
97 | {
98 | BeginPulsing = false;
99 | Pulsing = false;
100 | }
101 | OnsetsReached = 0;
102 | BeginPulsing = false;
103 |
104 | for (int i = OnsetIndex; i < Count; i++)
105 | {
106 | //Check for any 'hit' beats
107 | if (OnsetTimes[i] <= ElapsedGameTime + _onsetTimeBuffer) OnsetsReached++;
108 | //Check if need to pulse center polygon
109 | else if (!Pulsing && CloseToNextOnset(i, PulseDataCollection[i].PulseWidthMax / PulseDataCollection[i].PulseMultiplier))
110 | BeginPulsing = true;
111 | }
112 | }
113 |
114 | public bool CloseToNextOnset(int onsetIndex, double delta)
115 | {
116 | return (OnsetTimes[onsetIndex] - ElapsedGameTime) < delta;
117 | }
118 |
119 | public void Initialise()
120 | {
121 | OnsetIndex = 0;
122 | }
123 |
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/TurntNinja/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0
7 |
8 |
9 | 1
10 |
11 |
12 | 1280
13 |
14 |
15 | 720
16 |
17 |
18 | 4
19 |
20 |
21 | True
22 |
23 |
24 | Normal
25 |
26 |
27 | .mp3,.flac,.wav,.m4a,.wma
28 |
29 |
30 | turnt-ninja
31 |
32 |
33 | 2.5
34 |
35 |
36 | False
37 |
38 |
39 | 50
40 |
41 |
42 | 0
43 |
44 |
45 | False
46 |
47 |
48 | 0.2
49 |
50 |
51 | 10
52 |
53 |
54 | False
55 |
56 |
57 | True
58 |
59 |
60 | turnt-ninja.db
61 |
62 |
63 | opcon
64 |
65 |
66 |
67 |
68 |
69 | oeoywrZVrjYlcctPZZ3uTXFPp9ItVUkR3w28u+WDdd9SqYzE1PRkb+WPZsYQ+VvOfUjFQOlQ1tXc7oDXY4Km7A==
70 |
71 |
72 | jA9OAo1hWfHPOoMipHAAaFxFf2N7iP8zuBEI9LVQq5ET0vc8yqnLsO/oNMqCcki0rvD4CQrkMz7fWHQ4rWuHAyRfY+7nxGinxdbgIaceyVtKOJLsSnbBh4/NBq3OkBIAFG2GTxUhWqyJbxNhgzbu+0HM8jv43LY53bxhwIALt+A=
73 |
74 |
75 | False
76 |
77 |
78 | True
79 |
80 |
81 | 0
82 |
83 |
84 | False
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2012, Vernon Adams (vern@newtypography.co.uk), with Reserved Font Names 'Oswald'
2 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
3 | This license is copied below, and is also available with a FAQ at:
4 | http://scripts.sil.org/OFL
5 |
6 |
7 | -----------------------------------------------------------
8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9 | -----------------------------------------------------------
10 |
11 | PREAMBLE
12 | The goals of the Open Font License (OFL) are to stimulate worldwide
13 | development of collaborative font projects, to support the font creation
14 | efforts of academic and linguistic communities, and to provide a free and
15 | open framework in which fonts may be shared and improved in partnership
16 | with others.
17 |
18 | The OFL allows the licensed fonts to be used, studied, modified and
19 | redistributed freely as long as they are not sold by themselves. The
20 | fonts, including any derivative works, can be bundled, embedded,
21 | redistributed and/or sold with any software provided that any reserved
22 | names are not used by derivative works. The fonts and derivatives,
23 | however, cannot be released under any other type of license. The
24 | requirement for fonts to remain under this license does not apply
25 | to any document created using the fonts or their derivatives.
26 |
27 | DEFINITIONS
28 | "Font Software" refers to the set of files released by the Copyright
29 | Holder(s) under this license and clearly marked as such. This may
30 | include source files, build scripts and documentation.
31 |
32 | "Reserved Font Name" refers to any names specified as such after the
33 | copyright statement(s).
34 |
35 | "Original Version" refers to the collection of Font Software components as
36 | distributed by the Copyright Holder(s).
37 |
38 | "Modified Version" refers to any derivative made by adding to, deleting,
39 | or substituting -- in part or in whole -- any of the components of the
40 | Original Version, by changing formats or by porting the Font Software to a
41 | new environment.
42 |
43 | "Author" refers to any designer, engineer, programmer, technical
44 | writer or other person who contributed to the Font Software.
45 |
46 | PERMISSION & CONDITIONS
47 | Permission is hereby granted, free of charge, to any person obtaining
48 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
49 | redistribute, and sell modified and unmodified copies of the Font
50 | Software, subject to the following conditions:
51 |
52 | 1) Neither the Font Software nor any of its individual components,
53 | in Original or Modified Versions, may be sold by itself.
54 |
55 | 2) Original or Modified Versions of the Font Software may be bundled,
56 | redistributed and/or sold with any software, provided that each copy
57 | contains the above copyright notice and this license. These can be
58 | included either as stand-alone text files, human-readable headers or
59 | in the appropriate machine-readable metadata fields within text or
60 | binary files as long as those fields can be easily viewed by the user.
61 |
62 | 3) No Modified Version of the Font Software may use the Reserved Font
63 | Name(s) unless explicit written permission is granted by the corresponding
64 | Copyright Holder. This restriction only applies to the primary font name as
65 | presented to the users.
66 |
67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68 | Software shall not be used to promote, endorse or advertise any
69 | Modified Version, except to acknowledge the contribution(s) of the
70 | Copyright Holder(s) and the Author(s) or with their explicit written
71 | permission.
72 |
73 | 5) The Font Software, modified or unmodified, in part or in whole,
74 | must be distributed entirely under this license, and must not be
75 | distributed under any other license. The requirement for fonts to
76 | remain under this license does not apply to any document created
77 | using the Font Software.
78 |
79 | TERMINATION
80 | This license becomes null and void if any of the above conditions are
81 | not met.
82 |
83 | DISCLAIMER
84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92 | OTHER DEALINGS IN THE FONT SOFTWARE.
93 |
--------------------------------------------------------------------------------
/src/TurntNinja/Content/Fonts/Ostrich Sans/Open Font License.markdown:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, Tyler Finck , with Reserved Font Name: "Ostrich Sans".
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 | Version 1.1 - 26 February 2007
8 |
9 |
10 | SIL Open Font License
11 | ====================================================
12 |
13 |
14 | Preamble
15 | ----------
16 |
17 | The goals of the Open Font License (OFL) are to stimulate worldwide
18 | development of collaborative font projects, to support the font creation
19 | efforts of academic and linguistic communities, and to provide a free and
20 | open framework in which fonts may be shared and improved in partnership
21 | with others.
22 |
23 | The OFL allows the licensed fonts to be used, studied, modified and
24 | redistributed freely as long as they are not sold by themselves. The
25 | fonts, including any derivative works, can be bundled, embedded,
26 | redistributed and/or sold with any software provided that any reserved
27 | names are not used by derivative works. The fonts and derivatives,
28 | however, cannot be released under any other type of license. The
29 | requirement for fonts to remain under this license does not apply
30 | to any document created using the fonts or their derivatives.
31 |
32 | Definitions
33 | -------------
34 |
35 | `"Font Software"` refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | `"Reserved Font Name"` refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | `"Original Version"` refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | `"Modified Version"` refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | `"Author"` refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | Permission & Conditions
54 | ------------------------
55 |
56 | Permission is hereby granted, free of charge, to any person obtaining
57 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
58 | redistribute, and sell modified and unmodified copies of the Font
59 | Software, subject to the following conditions:
60 |
61 | 1. Neither the Font Software nor any of its individual components,
62 | in Original or Modified Versions, may be sold by itself.
63 |
64 | 2. Original or Modified Versions of the Font Software may be bundled,
65 | redistributed and/or sold with any software, provided that each copy
66 | contains the above copyright notice and this license. These can be
67 | included either as stand-alone text files, human-readable headers or
68 | in the appropriate machine-readable metadata fields within text or
69 | binary files as long as those fields can be easily viewed by the user.
70 |
71 | 3. No Modified Version of the Font Software may use the Reserved Font
72 | Name(s) unless explicit written permission is granted by the corresponding
73 | Copyright Holder. This restriction only applies to the primary font name as
74 | presented to the users.
75 |
76 | 4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font
77 | Software shall not be used to promote, endorse or advertise any
78 | Modified Version, except to acknowledge the contribution(s) of the
79 | Copyright Holder(s) and the Author(s) or with their explicit written
80 | permission.
81 |
82 | 5. The Font Software, modified or unmodified, in part or in whole,
83 | must be distributed entirely under this license, and must not be
84 | distributed under any other license. The requirement for fonts to
85 | remain under this license does not apply to any document created
86 | using the Font Software.
87 |
88 | Termination
89 | -----------
90 |
91 | This license becomes null and void if any of the above conditions are
92 | not met.
93 |
94 |
95 | DISCLAIMER
96 |
97 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
98 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
99 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
100 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
101 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
102 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
103 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
104 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
105 | OTHER DEALINGS IN THE FONT SOFTWARE.
106 |
--------------------------------------------------------------------------------
/src/TurntNinja/Logging/PiwikAnalytics.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Substructio.Logging;
7 | using Substructio.Core;
8 | using Piwik.Tracker;
9 |
10 | namespace TurntNinja.Logging
11 | {
12 | class PiwikAnalytics : IAnalytics
13 | {
14 | readonly string _piwikURL;
15 | readonly int _appID;
16 | readonly Platform _platform;
17 | readonly string _platformVersion;
18 | readonly int _resX;
19 | readonly int _resY;
20 | readonly string _gameVersion;
21 | readonly string _userID;
22 | readonly string _baseURL;
23 | readonly Uri _baseUri;
24 | readonly string _userAgent;
25 |
26 | private PiwikTracker _piwikTracker;
27 |
28 | public PiwikAnalytics(int appID, string piwikURL, Platform platform, string platformVersion, int resX, int resY, string gameVersion, string userID, string baseURL)
29 | {
30 | _piwikURL = piwikURL;
31 | _appID = appID;
32 | _platform = platform;
33 | _platformVersion = platformVersion;
34 | _resX = resX;
35 | _resY = resY;
36 | _gameVersion = gameVersion;
37 | _userID = userID;
38 | _baseURL = baseURL;
39 | _baseUri = new Uri(_baseURL);
40 |
41 | // Initialize tracking client
42 | _piwikTracker = new PiwikTracker(_appID, _piwikURL);
43 |
44 | // Cookies don't exist without an HTTP Context,
45 | // but disable them explicitly for peace of mind
46 | _piwikTracker.disableCookieSupport();
47 |
48 | // Set piwik timeout
49 | _piwikTracker.setRequestTimeout(10);
50 |
51 | // Set user agent
52 | _userAgent = BuildUserAgent();
53 | _piwikTracker.setUserAgent(_userAgent);
54 |
55 | // Set resolution
56 | _piwikTracker.setResolution(_resX, _resY);
57 |
58 | // Set anonymous user id
59 | _piwikTracker.setUserId(_userID);
60 |
61 | // Force new visit on first tracking call, since we've just
62 | // started the application, i.e. a visit
63 | _piwikTracker.setForceNewVisit();
64 |
65 | // Set default base url
66 | _piwikTracker.setUrl(_baseURL);
67 | }
68 |
69 | public void TrackApplicationStartup()
70 | {
71 | TrackApplicationView("startup", "Application Startup");
72 | }
73 |
74 | public void TrackApplicationShutdown()
75 | {
76 | TrackApplicationView("shutdown", "Application Shutdown");
77 | }
78 |
79 | public void TrackEvent(string eventCategory, string eventAction, string eventSubjectName = "", string eventValue = "")
80 | {
81 | try
82 | {
83 | // Set game version custom dimension for all tracking requests
84 | _piwikTracker.setCustomTrackingParameter("dimension1", _gameVersion);
85 |
86 | // Track the event
87 | var response = _piwikTracker.doTrackEvent(eventCategory, eventAction, eventSubjectName, eventValue);
88 |
89 | // Close response
90 | response.Close();
91 | }
92 | catch {}
93 | }
94 |
95 | public void TrackApplicationView(string relativeURL, string title="")
96 | {
97 | try
98 | {
99 | // Set game version custom dimension for all tracking requests
100 | _piwikTracker.setCustomTrackingParameter("dimension1", _gameVersion);
101 |
102 | // Set page "url"
103 | _piwikTracker.setUrl(new Uri(_baseUri, relativeURL).ToString());
104 |
105 | // Track the page view
106 | var response = _piwikTracker.doTrackPageView(title);
107 |
108 | // Close respone
109 | response.Close();
110 | }
111 | catch {}
112 | }
113 |
114 | public void SetCustomVariable(int variableID, string variableName, string variableValue, CustomVariableScope variableScope)
115 | {
116 | try
117 | {
118 | var scope = (variableScope == CustomVariableScope.ApplicationLaunch) ? CustomVar.Scopes.visit : CustomVar.Scopes.page;
119 | _piwikTracker.setCustomVariable(variableID, variableName, variableValue, scope);
120 | }
121 | catch {}
122 | }
123 |
124 | private string BuildUserAgent()
125 | {
126 | var os = "";
127 | switch (_platform)
128 | {
129 | case Platform.Windows:
130 | os = "Windows";
131 | break;
132 | case Platform.Linux:
133 | os = "Linux";
134 | break;
135 | case Platform.MacOSX:
136 | os = "Mac";
137 | break;
138 | }
139 | return $"{os} {_platformVersion}";
140 | }
141 |
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/TurntNinja/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | True
20 |
21 |
22 |
23 |
24 | True
25 |
26 |
27 |
28 |
29 | True
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 0
38 |
39 |
40 | 1
41 |
42 |
43 | 1280
44 |
45 |
46 | 720
47 |
48 |
49 | 4
50 |
51 |
52 | True
53 |
54 |
55 | Normal
56 |
57 |
58 | 2.5
59 |
60 |
61 | False
62 |
63 |
64 | 50
65 |
66 |
67 | 0
68 |
69 |
70 | False
71 |
72 |
73 | 0.2
74 |
75 |
76 | 10
77 |
78 |
79 | False
80 |
81 |
82 | True
83 |
84 |
85 | turnt-ninja.db
86 |
87 |
88 | opcon
89 |
90 |
91 |
92 |
93 |
94 | False
95 |
96 |
97 | True
98 |
99 |
100 | 0
101 |
102 |
103 | False
104 |
105 |
106 |
107 |
108 |
109 |
110 | .mp3,.flac,.wav,.m4a,.wma
111 |
112 |
113 | turnt-ninja
114 |
115 |
116 | oeoywrZVrjYlcctPZZ3uTXFPp9ItVUkR3w28u+WDdd9SqYzE1PRkb+WPZsYQ+VvOfUjFQOlQ1tXc7oDXY4Km7A==
117 |
118 |
119 | jA9OAo1hWfHPOoMipHAAaFxFf2N7iP8zuBEI9LVQq5ET0vc8yqnLsO/oNMqCcki0rvD4CQrkMz7fWHQ4rWuHAyRfY+7nxGinxdbgIaceyVtKOJLsSnbBh4/NBq3OkBIAFG2GTxUhWqyJbxNhgzbu+0HM8jv43LY53bxhwIALt+A=
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/src/TurntNinja/Audio/AudioFeatures.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using OnsetDetection;
8 | using CSCore;
9 | using System.Diagnostics;
10 |
11 | namespace TurntNinja.Audio
12 | {
13 | class AudioFeatures
14 | {
15 | private OnsetDetector _onsetDetector;
16 | string _csvDirectory;
17 | string _outputSuffix = "onsets";
18 |
19 | public List OnsetTimes = new List();
20 | public List Onsets = new List();
21 | public float _correction;
22 | private IProgress _outerProgressReporter;
23 | private IProgress _innerProgressReporter;
24 | private string _currentTask = "";
25 |
26 | public AudioFeatures(DetectorOptions options, string csvDirectory, float correction, IProgress progress = null)
27 | {
28 | //force garbage collection
29 | GC.Collect(2, GCCollectionMode.Forced, true);
30 |
31 | _csvDirectory = csvDirectory;
32 | _outerProgressReporter = progress ?? new Progress();
33 | _correction = correction;
34 | _innerProgressReporter = new Progress(status =>
35 | {
36 | _outerProgressReporter.Report(_currentTask + ":" + status);
37 | });
38 |
39 | _onsetDetector = new OnsetDetector(options, _innerProgressReporter);
40 | }
41 |
42 | public bool SongAnalysed(string audioPath)
43 | {
44 | return File.Exists(GetOnsetFilePath(audioPath));
45 | }
46 |
47 | public void Extract(CSCore.IWaveSource audioSource, Song s)
48 | {
49 | _currentTask = "Analysing Song";
50 |
51 | List onsets;
52 | if (SongAnalysed(s.SongBase.InternalName))
53 | onsets = LoadOnsets(GetOnsetFilePath(s.SongBase.InternalName));
54 | else
55 | {
56 | onsets = _onsetDetector.Detect(audioSource.ToSampleSource());
57 | SaveOnsets(GetOnsetFilePath(s.SongBase.InternalName), onsets);
58 | }
59 | OnsetTimes = onsets.Select(o => o.OnsetTime).ToList();
60 | Onsets = onsets;
61 | ApplyCorrection(OnsetTimes, _correction);
62 |
63 | //force garbage collection
64 | GC.Collect(2, GCCollectionMode.Forced, true);
65 |
66 | //audioSource.Position = 0;
67 | //AcoustID.ChromaContext cm = new AcoustID.ChromaContext();
68 | //AcoustIDCSCore decoder = new AcoustIDCSCore(audioSource);
69 | //cm.Start(audioSource.WaveFormat.SampleRate, audioSource.WaveFormat.Channels);
70 | //decoder.Decode(cm.Consumer, 60);
71 | //cm.Finish();
72 |
73 | //var f = cm.GetFingerprint();
74 |
75 | //Debug.WriteLine(f);
76 |
77 | //AcoustID.Configuration.ClientKey = "3jSfGwVIGZ";
78 | //AcoustID.Web.LookupService lService = new AcoustID.Web.LookupService();
79 | //var duration = audioSource.GetLength().TotalSeconds;
80 | //var lookupTask = lService.GetAsync(f, (int)duration, new string[] { "recordings", "compress" });
81 | //lookupTask.ContinueWith(t =>
82 | //{
83 | // var resp = t.Result;
84 | // foreach (var res in resp.Results)
85 | // {
86 | // Debug.WriteLine($"Score: {res.Score}, ID: {res.Id}");
87 | // foreach (var rec in res.Recordings)
88 | // {
89 | // Debug.WriteLine($"{rec.Artists.First().Name} - {rec.Title}");
90 | // }
91 | // }
92 | //});
93 | //decoder.Dispose();
94 | }
95 |
96 |
97 | private void SaveOnsets(string onsetFile, List onsets)
98 | {
99 | using (StreamWriter sw = new StreamWriter(onsetFile))
100 | {
101 | foreach (var onset in onsets)
102 | {
103 | sw.WriteLine(onset.ToString());
104 | }
105 | sw.Close();
106 | }
107 | }
108 |
109 | private List LoadOnsets(string onsetFile)
110 | {
111 | List onsets = new List();
112 | using (StreamReader sr = new StreamReader(onsetFile))
113 | {
114 | string line;
115 | while ((line = sr.ReadLine()) != null)
116 | {
117 | onsets.Add(new Onset { OnsetTime = float.Parse(line.Split(',')[0]), OnsetAmplitude = float.Parse(line.Split(',')[1]) });
118 | }
119 | sr.Close();
120 | }
121 | return onsets;
122 | }
123 |
124 | private void ApplyCorrection(List onsets, float correction)
125 | {
126 | for (int i = 0; i < onsets.Count; i++)
127 | {
128 | onsets[i] += correction;
129 | }
130 | }
131 |
132 | private string GetOnsetFilePath(string audioPath)
133 | {
134 | if (!Directory.Exists(_csvDirectory)) Directory.CreateDirectory(_csvDirectory);
135 | return Path.Combine(_csvDirectory, String.Format("{0}_{1}", Path.GetFileNameWithoutExtension(audioPath), _outputSuffix) + ".csv");
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/FirstRunScene.cs:
--------------------------------------------------------------------------------
1 | using Substructio.GUI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using QuickFont;
8 | using QuickFont.Configuration;
9 | using Substructio.Core;
10 | using OpenTK;
11 | using System.Drawing;
12 | using OpenTK.Graphics;
13 | using Substructio.Logging;
14 | using System.Diagnostics;
15 |
16 | namespace TurntNinja.GUI
17 | {
18 | class FirstRunScene : Scene
19 | {
20 | GameFont _bodyFont;
21 | GameFont _headerFont;
22 | QFontDrawing _fontDrawing;
23 |
24 | SizeF _headerSize;
25 | SizeF _bodyText1Size;
26 | SizeF _bodyListSize;
27 | SizeF _bodyText2Size;
28 |
29 | float _bodyTextWidth;
30 |
31 | string _headerText = "WELCOME TO TURNT NINJA";
32 |
33 | string _bodyList =
34 | "* Operating system version\n" +
35 | "* Game resolution\n" +
36 | "* Number of songs played\n" +
37 | "* Crash reports\n";
38 |
39 | string _bodyText1 =
40 | "Thank you for downloading my game!\nThis game has optional analytics " +
41 | "and crash reporting, which helps with development. The following "+
42 | "data is collected if you opt in:\n";
43 |
44 | string _bodyText2 =
45 | "\nPlease press Enter to opt in, or Escape to opt out.\n\nThank you,\nPatrick";
46 |
47 | public FirstRunScene()
48 | {
49 | Exclusive = true;
50 | }
51 |
52 | public override void Load()
53 | {
54 | _bodyFont = SceneManager.GameFontLibrary.GetFirstOrDefault("largebody");
55 | _headerFont = SceneManager.GameFontLibrary.GetFirstOrDefault(GameFontType.Menu);
56 |
57 | _fontDrawing = new QFontDrawing();
58 | _fontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
59 |
60 | _headerSize = _headerFont.Font.Measure(_headerText);
61 |
62 | _bodyTextWidth = 0.75f;
63 | _bodyText1Size = _bodyFont.Font.Measure(_bodyText1, _bodyTextWidth * WindowWidth, QFontAlignment.Centre);
64 | _bodyText2Size = _bodyFont.Font.Measure(_bodyText2, _bodyTextWidth * WindowWidth, QFontAlignment.Centre);
65 | _bodyListSize = _bodyFont.Font.Measure(_bodyList, _bodyTextWidth * WindowWidth, QFontAlignment.Left);
66 |
67 | ServiceLocator.Settings["FirstRun"] = false;
68 |
69 | var informationalVersionAttribute = System.Reflection.Assembly.GetExecutingAssembly().CustomAttributes.FirstOrDefault(cad => cad.AttributeType == typeof(System.Reflection.AssemblyInformationalVersionAttribute));
70 | string tag = ((string)informationalVersionAttribute.ConstructorArguments.First().Value).Split(' ')[0].Split(':')[1];
71 | if (tag.Length > 1)
72 | ServiceLocator.Settings["GetAlphaReleases"] = true;
73 |
74 | Loaded = true;
75 | }
76 |
77 | public override void CallBack(GUICallbackEventArgs e)
78 | {
79 | }
80 |
81 | public override void Dispose()
82 | {
83 | _fontDrawing.Dispose();
84 | }
85 |
86 | public override void Draw(double time)
87 | {
88 | _fontDrawing.RefreshBuffers();
89 | _fontDrawing.Draw();
90 | }
91 |
92 | public override void Resize(EventArgs e)
93 | {
94 | _fontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
95 | }
96 |
97 | public override void Update(double time, bool focused = false)
98 | {
99 | if (InputSystem.NewKeys.Contains(OpenTK.Input.Key.Enter))
100 | {
101 | ServiceLocator.Settings["Analytics"] = true;
102 | ServiceLocator.Settings.Save();
103 |
104 | // Track application startup
105 | ServiceLocator.Analytics.TrackApplicationStartup();
106 |
107 | SceneManager.RemoveScene(this, true);
108 | }
109 | else if (InputSystem.NewKeys.Contains(OpenTK.Input.Key.Escape))
110 | {
111 | ServiceLocator.Settings["Analytics"] = false;
112 |
113 | // Disable analytics and error reporting
114 | ServiceLocator.Analytics = new NullAnalytics();
115 | ServiceLocator.ErrorReporting = new NullErrorReporting();
116 |
117 | SceneManager.RemoveScene(this, true);
118 | }
119 |
120 | _fontDrawing.DrawingPrimitives.Clear();
121 |
122 | var headerOffset = (WindowHeight/2.0f - WindowHeight/12.0f);
123 | _fontDrawing.Print(_headerFont.Font, _headerText, new Vector3(0, headerOffset + (_headerSize.Height / 2.0f), 0), QFontAlignment.Centre, Color.White);
124 | _fontDrawing.Print(_bodyFont.Font, _bodyText1, new Vector3(0, headerOffset - (_headerSize.Height), 0), new SizeF(WindowWidth * _bodyTextWidth, -1), QFontAlignment.Centre);
125 | _fontDrawing.Print(_bodyFont.Font, _bodyList, new Vector3(-WindowWidth*0.25f, (headerOffset - (_headerSize.Height)) - _bodyText1Size.Height, 0), new SizeF(WindowWidth * _bodyTextWidth, -1), QFontAlignment.Left);
126 | _fontDrawing.Print(_bodyFont.Font, _bodyText2, new Vector3(0, (headerOffset - (_headerSize.Height)) - _bodyText1Size.Height - _bodyListSize.Height, 0), new SizeF(WindowWidth * _bodyTextWidth, -1), QFontAlignment.Centre);
127 | }
128 |
129 | public override void EnterFocus()
130 | {
131 | }
132 |
133 | public override void ExitFocus()
134 | {
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/ChooseSongScene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using TurntNinja.Core;
9 | using Substructio.Core;
10 | using Substructio.Graphics.OpenGL;
11 | using Substructio.GUI;
12 | using Key = OpenTK.Input.Key;
13 | using OpenTK;
14 | using OpenTK.Graphics;
15 | using TurntNinja.FileSystem;
16 | using TurntNinja.Audio;
17 | using System.Xml.Serialization;
18 | using Substructio;
19 |
20 | namespace TurntNinja.GUI
21 | {
22 | class ChooseSongScene : Scene
23 | {
24 | private GUIComponentContainer _guiComponents;
25 | private readonly PolarPolygon _centerPolygon;
26 | private readonly Player _player;
27 | private readonly ShaderProgram _shaderProgram;
28 | private List _recentSongs;
29 |
30 | private const string RECENT_SONGS_FILE = "recent-songs.xml";
31 | private string _recentSongsFile = "";
32 |
33 | DirectoryBrowser _directoryBrowser;
34 |
35 | public ChooseSongScene(GUIComponentContainer guiComponents, PolarPolygon centerPolygon, Player player, ShaderProgram shaderProgram)
36 | {
37 | _guiComponents = guiComponents;
38 | _centerPolygon = centerPolygon;
39 | _player = player;
40 | _shaderProgram = shaderProgram;
41 | Exclusive = true;
42 | }
43 |
44 | public override void Load()
45 | {
46 | _directoryBrowser = new DirectoryBrowser(SceneManager, this);
47 | _directoryBrowser.AddFileSystem(new LocalFileSystem(SceneManager.Directories));
48 | _directoryBrowser.AddFileSystem(new SoundCloudFileSystem());
49 |
50 | // Find recent songs file path
51 | _recentSongsFile = ServiceLocator.Directories.Locate("AppData", RECENT_SONGS_FILE);
52 |
53 | // Load recent songs
54 | LoadRecentSongs();
55 |
56 | // Make sure to add recent file system last!
57 | _directoryBrowser.AddFileSystem(new RecentFileSystem(_recentSongs));
58 |
59 | _directoryBrowser.SwitchCurrentFileSystemIfEmpty();
60 |
61 | _directoryBrowser.Resize(WindowWidth, WindowHeight);
62 | Loaded = true;
63 | }
64 |
65 | private void LoadRecentSongs()
66 | {
67 | if (File.Exists(_recentSongsFile))
68 | {
69 | var serializer = new XmlSerializer(typeof(List));
70 | using (TextReader f = new StreamReader(_recentSongsFile))
71 | {
72 | _recentSongs = (List)serializer.Deserialize(f);
73 | }
74 | }
75 | else
76 | _recentSongs = new List();
77 | }
78 |
79 | private void SaveRecentSongs()
80 | {
81 | var serializer = new XmlSerializer(typeof(List));
82 | using (TextWriter f = new StreamWriter(_recentSongsFile))
83 | {
84 | serializer.Serialize(f, _recentSongs);
85 | }
86 | }
87 |
88 | public void SongChosen(Song song)
89 | {
90 | // Track song play with analytics
91 | ServiceLocator.Analytics.SetCustomVariable(1, "File System", song.FileSystem.FriendlyName, Substructio.Logging.CustomVariableScope.ApplicationView);
92 | ServiceLocator.Analytics.TrackEvent("Song", "Play", song.FileSystem.FriendlyName);
93 |
94 | // Remove the currently selected song if it already exists in the recent songs list
95 | // so that we can insert it again at the head, retaining the list order
96 | _recentSongs.Remove(song.SongBase);
97 |
98 | // Remove the oldest song if we are over the maximum recent song count
99 | if (_recentSongs.Count >= (int)SceneManager.GameSettings["MaxRecentSongCount"])
100 | _recentSongs.RemoveAt(_recentSongs.Count - 1);
101 |
102 | // Insert the new song at the head of the list
103 | _recentSongs.Insert(0, song.SongBase);
104 |
105 | // Save recent songs file
106 | SaveRecentSongs();
107 |
108 | // Refresh recent songs filesystem
109 | _directoryBrowser.RefreshRecentSongFilesystem();
110 |
111 | //SceneManager.RemoveScene(this);
112 | this.Visible = false;
113 | SceneManager.AddScene(
114 | new LoadingScene(
115 | (float)SceneManager.GameSettings["AudioCorrection"],
116 | (float)SceneManager.GameSettings["MaxAudioVolume"], _centerPolygon, _player, _shaderProgram, song), this);
117 | }
118 |
119 | public override void CallBack(GUICallbackEventArgs e)
120 | {
121 | }
122 |
123 | public override void Resize(EventArgs e)
124 | {
125 | _directoryBrowser.Resize(WindowWidth, WindowHeight);
126 | }
127 |
128 | public override void Update(double time, bool focused = false)
129 | {
130 | if (focused)
131 | {
132 | if (InputSystem.NewKeys.Contains(Key.Escape)) this.Visible = false;
133 |
134 | _directoryBrowser.Update(time);
135 | }
136 | }
137 |
138 | public override void Draw(double time)
139 | {
140 | _directoryBrowser.Draw(time);
141 | }
142 |
143 | public override void Dispose()
144 | {
145 | }
146 |
147 | public override void EnterFocus()
148 | {
149 | InputSystem.RepeatingKeys.Add(Key.Down, KeyRepeatSettings.Default);
150 | InputSystem.RepeatingKeys.Add(Key.Up, KeyRepeatSettings.Default);
151 | InputSystem.RepeatingKeys.Add(Key.BackSpace, KeyRepeatSettings.Default);
152 | }
153 |
154 | public override void ExitFocus()
155 | {
156 | InputSystem.RepeatingKeys.Remove(Key.Down);
157 | InputSystem.RepeatingKeys.Remove(Key.Up);
158 | InputSystem.RepeatingKeys.Remove(Key.BackSpace);
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/GameScene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Drawing;
5 | using TurntNinja.Core;
6 | using TurntNinja.Game;
7 | using OpenTK;
8 | using OpenTK.Graphics;
9 | using OpenTK.Graphics.OpenGL4;
10 | using QuickFont;
11 | using Substructio.Graphics.OpenGL;
12 | using Substructio.GUI;
13 | using Gwen;
14 | using Substructio.Core;
15 | using Key = OpenTK.Input.Key;
16 |
17 | namespace TurntNinja.GUI
18 | {
19 | class GameScene : Scene
20 | {
21 | private Stage _stage;
22 | private Stage _backStage;
23 | private ProcessedText _multiplerText;
24 | public ShaderProgram ShaderProgram { get; set; }
25 | public bool UsingPlaylist { get; set; }
26 | public List PlaylistFiles { get; set; }
27 |
28 | private const float TIMETOWAIT = 1.0f;
29 |
30 | private double _elapsedTime = 0;
31 |
32 | public GameScene(Stage stage)
33 | {
34 | Exclusive = true;
35 | _stage = stage;
36 | //_stage.StageGeometry.UpdateColours(0);
37 | _stage.Initialise();
38 | }
39 |
40 | public override void Load()
41 | {
42 | SceneManager.ScreenCamera.Scale = SceneManager.ScreenCamera.TargetScale = new Vector2(1.5f);
43 | if (UsingPlaylist)
44 | {
45 |
46 | }
47 | Loaded = true;
48 | }
49 |
50 | private void Exit(bool GoToEndScene = false)
51 | {
52 | _stage.Reset(!GoToEndScene);
53 | SceneManager.ScreenCamera.ExtraScale = 0;
54 | SceneManager.ScreenCamera.Scale = SceneManager.ScreenCamera.TargetScale = new Vector2(1, 1);
55 | SceneManager.GameWindow.Cursor = MouseCursor.Default;
56 | if (GoToEndScene) SceneManager.AddScene(new EndGameScene(_stage), this);
57 | SceneManager.RemoveScene(this, !GoToEndScene);
58 | }
59 |
60 | public override void CallBack(GUICallbackEventArgs e)
61 | {
62 | throw new NotImplementedException();
63 | }
64 |
65 | public override void Resize(EventArgs e)
66 | {
67 | _stage.MultiplierFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
68 | _stage.ScoreFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
69 | }
70 |
71 | public override void Update(double time, bool focused = false)
72 | {
73 | if (InputSystem.NewKeys.Contains(Key.Escape))
74 | {
75 | Exit();
76 | return;
77 | }
78 | _stage.Update(time);
79 |
80 | if (_stage.Ended && (_stage.TotalTime - _stage.EndTime) > TIMETOWAIT)
81 | {
82 | Exit(true);
83 | }
84 | }
85 |
86 | public override void Draw(double time)
87 | {
88 | _elapsedTime += time;
89 | var rot = Matrix4.CreateRotationX((float)((MathHelper.PiOver4 / 1.25)*Math.Sin((_elapsedTime*0.20))));
90 | ShaderProgram.Bind();
91 | ShaderProgram.SetUniform("mvp", Matrix4.Mult(rot, SceneManager.ScreenCamera.WorldModelViewProjection));
92 | _stage.Draw(time);
93 | //Cleanup the program
94 | ShaderProgram.UnBind();
95 |
96 | if (SceneManager.Debug)
97 | {
98 | float yOffset = -SceneManager.Height * 0.5f + 30f;
99 | float xOffset = -SceneManager.Width * 0.5f + 20;
100 | xOffset += SceneManager.DrawTextLine(_stage.Overlap.ToString(), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Width + 20;
101 | xOffset += SceneManager.DrawTextLine(_stage.Hits.ToString(), new Vector3(xOffset, yOffset, 0), Color.Red, QFontAlignment.Left).Width + 20;
102 | xOffset += SceneManager.DrawTextLine(string.Format("{0}/{1}", _stage.CurrentPolygon, _stage.PolygonCount), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Width + 20;
103 | //xOffset +=
104 | // SceneManager.Font.Print(string.Format("{0}/{1}", SceneManager.ScreenCamera.TargetScale, SceneManager.ScreenCamera.Scale), new Vector3(xOffset, yOffset, 0), QFontAlignment.Left,
105 | // Color.White).Width + 20;
106 | //xOffset += SceneManager.Font.Print(string.Format("Current score is {0}", _stage.StageGeometry.Player.Score), new Vector3(xOffset, yOffset, 0), QFontAlignment.Left, Color.White).Width + 20;
107 | //xOffset += SceneManager.Font.Print(string.Format("Scale is {0}", SceneManager.ScreenCamera.Scale), new Vector3(xOffset, yOffset, 0), QFontAlignment.Left, Color.White).Width + 20;
108 | //xOffset += SceneManager.Font.Print(string.Format("Pulse Multiplier is {0}", _stage.StageGeometry.CenterPolygon.PulseMultiplier), new Vector3(xOffset, yOffset, 0), QFontAlignment.Left, Color.White).Width + 20;
109 | xOffset += SceneManager.DrawTextLine(string.Format("Mouse coordinates are {0}", InputSystem.MouseXY), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Width + 20;
110 | xOffset += SceneManager.DrawTextLine(string.Format("Song Playing {0}", !_stage._stageAudio.IsStopped), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Width + 20;
111 | xOffset += SceneManager.DrawTextLine(string.Format("Beat Frequency {0}", _stage.StageGeometry.CurrentBeatFrequency), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Width + 20;
112 |
113 | yOffset = SceneManager.Height * 0.5f;
114 | xOffset = -SceneManager.Width * 0.5f + 20;
115 | yOffset -= SceneManager.DrawTextLine(_stage.StageGeometry.ColourModifiers.ToString(), new Vector3(xOffset, yOffset, 0), Color.White, QFontAlignment.Left).Height + 20;
116 | }
117 | }
118 |
119 | public override void Dispose()
120 | {
121 | _stage.Reset(true);
122 | _stage.Dispose();
123 | _stage = null;
124 | }
125 |
126 | public override void EnterFocus()
127 | {
128 | }
129 |
130 | public override void ExitFocus()
131 | {
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/TurntNinja/Game/Player.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using ClipperLib;
5 | using OpenTK;
6 | using OpenTK.Graphics;
7 | using OpenTK.Input;
8 | using OpenTK.Graphics.OpenGL4;
9 | using Substructio.Core;
10 | using Substructio.Core.Math;
11 | using Substructio.Graphics.OpenGL;
12 |
13 | namespace TurntNinja
14 | {
15 | class Player
16 | {
17 | private double _length;
18 | private double _width;
19 |
20 | private PolarVector _position;
21 | private PolarVector _velocity;
22 |
23 | public int Hits;
24 | public Color4 Colour = Color4.White;
25 |
26 | private VertexBuffer _vertexBuffer;
27 | private VertexArray _vertexArray;
28 | private BufferDataSpecification _dataSpecification;
29 |
30 | public bool UseGamePad { get; set; }
31 |
32 | public ShaderProgram ShaderProgram
33 | {
34 | get { return _shaderProgram; }
35 | set
36 | {
37 | _shaderProgram = value;
38 | CreateBuffers();
39 | }
40 | }
41 |
42 | public double Length
43 | {
44 | get { return _length; }
45 | }
46 |
47 | public double Width
48 | {
49 | get { return _width; }
50 | }
51 |
52 | public PolarVector Position
53 | {
54 | get { return _position; }
55 | set { _position = value; }
56 | }
57 |
58 | public int Direction { get; set; }
59 |
60 | public PolarVector Velocity
61 | {
62 | get { return _velocity; }
63 | }
64 |
65 | public bool IsSlow { get { return _currentFramesInput.HasFlag(Input.Slow); } }
66 |
67 | public float Score;
68 |
69 | private Input _currentFramesInput;
70 | private ShaderProgram _shaderProgram;
71 |
72 | const float PLAYER_RADIUS = 180f;
73 | const float PLAYER_WIDTH = 20;
74 | const float PLAYER_LENGTH_DEGREES = 10;
75 | const float SLOW_MULTIPLIER = 0.5f;
76 |
77 | public Player()
78 | {
79 | _length = MathHelper.DegreesToRadians(PLAYER_LENGTH_DEGREES);
80 | _width = PLAYER_WIDTH;
81 | _position = new PolarVector(1.5 * (Math.PI / 3) - _length * 0.5f, PLAYER_RADIUS);
82 | _velocity = new PolarVector(-9, 0);
83 | Direction = 1;
84 | UseGamePad = false;
85 | }
86 |
87 | public void Update(double time, bool AI = false)
88 | {
89 | if (!AI) _currentFramesInput = GetUserInput();
90 | if (_currentFramesInput.HasFlag(Input.Slow))
91 | time *= SLOW_MULTIPLIER;
92 | // _position.Azimuth += time*0.5*Direction;
93 | if (_currentFramesInput.HasFlag(Input.Left))
94 | {
95 | _position.Azimuth -= _velocity.Azimuth*time;
96 | }
97 | else if (_currentFramesInput.HasFlag(Input.Right))
98 | {
99 | _position.Azimuth += _velocity.Azimuth*time;
100 | }
101 | _position = _position.Normalised();
102 |
103 | _vertexBuffer.Bind();
104 | _vertexBuffer.Initialise();
105 | _vertexBuffer.SetData(BuildVertexList(), _dataSpecification);
106 | _vertexBuffer.UnBind();
107 | }
108 |
109 | private void CreateBuffers()
110 | {
111 | _dataSpecification = new BufferDataSpecification
112 | {
113 | Count = 2,
114 | Name = "in_position",
115 | Offset = 0,
116 | ShouldBeNormalised = false,
117 | Stride = 0,
118 | Type = VertexAttribPointerType.Float
119 | };
120 |
121 | _vertexArray = new VertexArray{DrawPrimitiveType = PrimitiveType.Triangles};
122 | _vertexArray.Bind();
123 |
124 | var size = 3*2*sizeof (float);
125 | _vertexBuffer = new VertexBuffer
126 | {
127 | BufferUsage = BufferUsageHint.StreamDraw,
128 | DrawableIndices = 3,
129 | MaxSize = size
130 | };
131 | _vertexBuffer.Bind();
132 | _vertexBuffer.Initialise();
133 | _vertexBuffer.DataSpecifications.Add(_dataSpecification);
134 |
135 | _vertexArray.Load(_shaderProgram, _vertexBuffer);
136 | _vertexArray.UnBind();
137 | }
138 |
139 | public void Reset()
140 | {
141 | Score = 0;
142 | Hits = 0;
143 | _position = new PolarVector(1.5 * (Math.PI / 3) - _length * 0.5f, PLAYER_RADIUS);
144 | }
145 |
146 | public List GetBounds()
147 | {
148 | var p = new List();
149 | var p1 = PolarVector.ToCartesianCoordinates(_position);
150 | var p2 = PolarVector.ToCartesianCoordinates(_position, _length/2, _width);
151 | var p3 = PolarVector.ToCartesianCoordinates(_position, _length, 0);
152 |
153 | p.Add(new IntPoint(p1.X, p1.Y));
154 | p.Add(new IntPoint(p2.X, p2.Y));
155 | p.Add(new IntPoint(p3.X, p3.Y));
156 |
157 | return p;
158 | }
159 |
160 | public List BuildVertexList()
161 | {
162 | var verts = new List();
163 | verts.Add(PolarVector.ToCartesianCoordinates(_position));
164 | verts.Add(PolarVector.ToCartesianCoordinates(_position, _length/2, _width));
165 | verts.Add(PolarVector.ToCartesianCoordinates(_position, _length, 0));
166 | return verts.SelectMany(v => new[] {v.X, v.Y}).ToList();
167 | }
168 |
169 | public void Draw(double time)
170 | {
171 | _vertexArray.Draw(time);
172 | }
173 |
174 | private Input GetUserInput()
175 | {
176 | Input i = Input.Default;
177 | //if (GamePad.GetCapabilities(0).IsConnected)
178 | //{
179 | // if (OpenTK.Input.GamePad.GetState(0).Buttons.LeftShoulder == ButtonState.Pressed || GamePad.GetState(0).Triggers.Left > 0.3)
180 | // i |= Input.Left;
181 | // if (GamePad.GetState(0).Buttons.RightShoulder == ButtonState.Pressed || GamePad.GetState(0).Triggers.Right > 0.3)
182 | // i |= Input.Right;
183 | //}
184 | if (InputSystem.CurrentKeys.Contains(Key.Left))
185 | i |= Input.Left;
186 | if (InputSystem.CurrentKeys.Contains(Key.Right))
187 | i |= Input.Right;
188 | if (InputSystem.CurrentKeys.Contains(Key.Up))
189 | i |= Input.Up;
190 | if (InputSystem.CurrentKeys.Contains(Key.Down))
191 | i |= Input.Down;
192 | if (InputSystem.CurrentKeys.Contains(Key.ShiftLeft))
193 | i |= Input.Slow;
194 | return i;
195 | }
196 |
197 | public void DoAI(double targetAzimuth)
198 | {
199 | _currentFramesInput = DirectionToTurn(_position.Azimuth, targetAzimuth);
200 | }
201 |
202 | private Input DirectionToTurn(double current, double target)
203 | {
204 | current = MathUtilities.Normalise(current, 0, MathUtilities.TwoPI);
205 | target = MathUtilities.Normalise(target, 0, MathUtilities.TwoPI);
206 |
207 | var diff = Math.Abs(current - target);
208 | if (diff < 0.1)
209 | return Input.Default;
210 | int flip = 1;
211 | if (diff > Math.PI)
212 | flip *= -1;
213 | int d = current > target ? -flip : flip;
214 | return d > 0 ? Input.Left : Input.Right;
215 | }
216 | }
217 |
218 | [Flags]
219 | public enum Input
220 | {
221 | Default = 0,
222 | Left = 1,
223 | Right = 2,
224 | Up = 4,
225 | Down = 8,
226 | Slow = 16
227 | }
228 |
229 | }
230 |
--------------------------------------------------------------------------------
/src/TurntNinja/FileSystem/SoundCloudFileSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using TurntNinja.Audio;
8 | using SoundCloud.API.Client;
9 | using SoundCloud.API.Client.Objects;
10 | using System.Net;
11 |
12 | namespace TurntNinja.FileSystem
13 | {
14 | class SoundCloudFileSystem : IFileSystem
15 | {
16 | List _soundcloudSongs;
17 | List _scTracks;
18 | List _scCategories;
19 | FileBrowserEntry _entrySeparator;
20 | string clientID = "74e6e3acb28021e21eb32ef4bc10e995";
21 | string clientSecret = "";
22 | ISoundCloudConnector _sconnector;
23 | IUnauthorizedSoundCloudClient _scclient;
24 |
25 | const int FILE_SYSTEM_ENTRY_OFFSET = 2;
26 |
27 | public List FileSystemCollection { get; set; }
28 | public ReadOnlyCollection FileSystemEntryCollection { get { return _soundcloudSongs.AsReadOnly(); } }
29 |
30 | public string FriendlyName { get { return "Sound Cloud"; } }
31 |
32 | private object _lock = new object();
33 |
34 | public SoundCloudFileSystem()
35 | {
36 | _soundcloudSongs = new List();
37 | _scTracks = new List();
38 | _scCategories = new List();
39 | }
40 |
41 | public int Initialise(FileBrowserEntry separator)
42 | {
43 | _entrySeparator = separator;
44 |
45 | //setup soundcloud connection
46 | _sconnector = new SoundCloudConnector();
47 |
48 | _scclient = _sconnector.UnauthorizedConnect(clientID, clientSecret);
49 |
50 | //Get tracks
51 | Task.Run(() =>
52 | {
53 | lock (_lock )
54 | {
55 | //_scCategories = _scclient.Explore.GetExploreCategories().ToList();
56 | _scCategories = GetSoundcloudCategories();
57 | ShowCategories();
58 | }
59 | });
60 |
61 | return 0;
62 | }
63 |
64 | public void ShowCategories()
65 | {
66 | _soundcloudSongs = _scCategories.ConvertAll(c => new FileBrowserEntry { EntryType = FileBrowserEntryType.Directory, Name = System.Uri.UnescapeDataString(c.Name).Replace('+',' '), Path = c.Name});
67 | }
68 |
69 | public void ShowSongs()
70 | {
71 | _soundcloudSongs = _scTracks.ConvertAll(s => new FileBrowserEntry { EntryType = FileBrowserEntryType.Song, Name = s.Title, Path = s.Uri });
72 | }
73 |
74 | public bool EntrySelected(ref int entryIndex)
75 | {
76 | //return true if we've found a song
77 | if (_soundcloudSongs[entryIndex].EntryType.HasFlag(FileBrowserEntryType.Song))
78 | return true;
79 |
80 | //If category list selected
81 | if (_soundcloudSongs[entryIndex].EntryType.HasFlag(FileBrowserEntryType.Special))
82 | {
83 | ShowCategories();
84 | return false;
85 | }
86 |
87 | //If category selected
88 | _scTracks = _scclient.Chart.GetTracks(_scCategories[entryIndex]).ToList();
89 | ShowSongs();
90 |
91 | //Add the category list entry
92 | _soundcloudSongs.Insert(0, new FileBrowserEntry
93 | {
94 | EntryType = FileBrowserEntryType.Special,
95 | Name = "Category List",
96 | Path = ""
97 | });
98 |
99 | _soundcloudSongs.Insert(1, _entrySeparator);
100 |
101 | entryIndex = 0;
102 | return false;
103 | }
104 |
105 | public void LoadSongAudio(Song song)
106 | {
107 | var sctrack = _scclient.Resolve.GetTrack(song.SongBase.InternalName);
108 | var url = sctrack.StreamUrl + "?client_id=" + clientID;
109 | var wr = WebRequest.Create(url);
110 | var response = wr.GetResponse();
111 | song.SongAudio = CSCore.Codecs.CodecFactory.Instance.GetCodec(response.ResponseUri);
112 | }
113 |
114 | public Song LoadSongInformation(int entryIndex)
115 | {
116 | var sc = _scTracks[entryIndex - FILE_SYSTEM_ENTRY_OFFSET];
117 | return new Song
118 | {
119 | FileSystem = this,
120 | SongBase = new SongBase
121 | {
122 | InternalName = sc.Uri,
123 | Artist = sc.User.UserName,
124 | Identifier = sc.Title,
125 | TrackName = sc.Title,
126 | FileSystemFriendlyName = FriendlyName,
127 | }
128 | };
129 | }
130 |
131 | public bool SongExists(SongBase song)
132 | {
133 | //return _scclient.Resolve.GetTrack(song.InternalName) != null;
134 | return true;
135 | }
136 |
137 | public void Focused()
138 | {
139 | }
140 |
141 | public static List GetSoundcloudCategories()
142 | {
143 | return new List
144 | {
145 | new SCExploreCategory { Name = "Popular+Music" },
146 | new SCExploreCategory { Name = "Alternative+Rock" },
147 | new SCExploreCategory { Name = "Ambient" },
148 | new SCExploreCategory { Name = "Classical" },
149 | new SCExploreCategory { Name = "Country" },
150 | new SCExploreCategory { Name = "Dance+&+EDM" },
151 | new SCExploreCategory { Name = "Dancehall" },
152 | new SCExploreCategory { Name = "Deep+House" },
153 | new SCExploreCategory { Name = "Disco" },
154 | new SCExploreCategory { Name = "Drum+&+Bass" },
155 | new SCExploreCategory { Name = "Dubstep" },
156 | new SCExploreCategory { Name = "Electronic" },
157 | new SCExploreCategory { Name = "Folk+&+Singer-Songwriter" },
158 | new SCExploreCategory { Name = "Hip-Hop+&+Rap" },
159 | new SCExploreCategory { Name = "House" },
160 | new SCExploreCategory { Name = "Indie" },
161 | new SCExploreCategory { Name = "Jazz+&+Blues" },
162 | new SCExploreCategory { Name = "Latin" },
163 | new SCExploreCategory { Name = "Metal" },
164 | new SCExploreCategory { Name = "Piano" },
165 | new SCExploreCategory { Name = "Pop" },
166 | new SCExploreCategory { Name = "R&B+&+soul" },
167 | new SCExploreCategory { Name = "Reggae" },
168 | new SCExploreCategory { Name = "Reggaeton" },
169 | new SCExploreCategory { Name = "Rock" },
170 | new SCExploreCategory { Name = "Soundtrack" },
171 | new SCExploreCategory { Name = "Techno" },
172 | new SCExploreCategory { Name = "Trance" },
173 | new SCExploreCategory { Name = "Trap" },
174 | new SCExploreCategory { Name = "Triphop" },
175 | new SCExploreCategory { Name = "World" },
176 | };
177 | }
178 |
179 | internal void Search(string searchString)
180 | {
181 | _scTracks = _scclient.Tracks.BeginSearch(SoundCloud.API.Client.Objects.TrackPieces.SCFilter.All).Query(searchString).Exec().ToList();
182 |
183 | ShowSongs();
184 |
185 | //Add the category list entry
186 | _soundcloudSongs.Insert(0, new FileBrowserEntry
187 | {
188 | EntryType = FileBrowserEntryType.Special,
189 | Name = "Category List",
190 | Path = ""
191 | });
192 |
193 | _soundcloudSongs.Insert(1, _entrySeparator);
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/EndGameScene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using OpenTK;
8 | using OpenTK.Input;
9 | using QuickFont;
10 | using QuickFont.Configuration;
11 | using Substructio.Core;
12 | using Substructio.GUI;
13 | using TurntNinja.Game;
14 | using System.IO;
15 | using LiteDB;
16 | using System.Globalization;
17 |
18 | namespace TurntNinja.GUI
19 | {
20 | class EndGameScene : Scene
21 | {
22 | private GameFont _font;
23 | private QFontDrawing _fontDrawing;
24 | private SizeF _endGameTextSize;
25 | private string _endGameText = "Press Enter to Continue";
26 | private Stage _stage;
27 | private HighScoreEntry _highScoreEntry;
28 | private bool _newHighScore = false;
29 | private PlayerScore _newScore;
30 | private PlayerScore _highestScore;
31 |
32 | public EndGameScene(Stage stage)
33 | {
34 | _stage = stage;
35 | Exclusive = true;
36 | }
37 |
38 | public override void Load()
39 | {
40 | _font = SceneManager.GameFontLibrary.GetFirstOrDefault("selected");
41 | _fontDrawing = new QFontDrawing();
42 | SceneManager.RemoveScene(ParentScene);
43 | SceneManager.ScreenCamera.ExtraScale = 0;
44 | SceneManager.ScreenCamera.Scale = SceneManager.ScreenCamera.TargetScale = new Vector2(1, 1);
45 |
46 | //save song to db
47 | string dbFile = Path.Combine(SceneManager.Directories["AppData"].FullName, (string)SceneManager.GameSettings["DatabaseFile"]);
48 |
49 | // Need to check for old version of database
50 | try
51 | {
52 | SaveHighScore(dbFile);
53 | }
54 | catch (Exception ex)
55 | {
56 | // this is an old version of the database, delete the file and try again
57 | try
58 | {
59 | File.Delete(dbFile);
60 | }
61 | catch (Exception) { }
62 | }
63 |
64 | // This shouldn't fail, because we checked for old version of the database
65 | SaveHighScore(dbFile);
66 |
67 | UpdateText();
68 | Loaded = true;
69 | }
70 |
71 | private void SaveHighScore(string dbFile)
72 | {
73 | var cs = new ConnectionString();
74 | cs.Filename = dbFile;
75 | cs.Upgrade = true;
76 | using (var db = new LiteDatabase(cs))
77 | {
78 | var highSccoreCollection = db.GetCollection("highscores");
79 | long hash = (long)Utilities.FNV1aHash64(Encoding.Default.GetBytes(_stage.CurrentSong.SongBase.InternalName));
80 |
81 | //does this song exist in the database?
82 | if (highSccoreCollection.Exists(Query.And(Query.EQ("SongID", hash), Query.EQ("DifficultyLevel", _stage.CurrentDifficulty.ToString()))))
83 | {
84 | _highScoreEntry = highSccoreCollection.FindOne(Query.EQ("SongID", hash));
85 | }
86 | else
87 | {
88 | _highScoreEntry = new HighScoreEntry { SongID = hash, SongName = _stage.CurrentSong.SongBase.Identifier, Difficulty = _stage.CurrentDifficulty };
89 | }
90 |
91 | _highestScore = _highScoreEntry.HighScores.Count > 0 ? _highScoreEntry.HighScores.OrderByDescending(ps => ps.Score).First() : new PlayerScore();
92 | _newScore = new PlayerScore
93 | {
94 | Name = (string)SceneManager.GameSettings["PlayerName"],
95 | Accuracy = 100 - ((float)_stage.Hits / _stage.StageGeometry.OnsetCount) * 100.0f,
96 | Score = (long)_stage.StageGeometry.Player.Score
97 | };
98 |
99 | _highScoreEntry.HighScores.Add(_newScore);
100 | _highScoreEntry.HighScores.OrderByDescending(ps => ps.Score);
101 |
102 | // Save to DB
103 | using (var trans = db.BeginTrans())
104 | {
105 | if (_highScoreEntry.Id == null || !highSccoreCollection.Update(_highScoreEntry)) highSccoreCollection.Insert(_highScoreEntry);
106 | }
107 |
108 | if (_newScore.Score > _highestScore.Score)
109 | {
110 | _highestScore = _newScore;
111 | _newHighScore = true;
112 | }
113 | }
114 | }
115 |
116 | public override void CallBack(GUICallbackEventArgs e)
117 | {
118 | }
119 |
120 | public override void Resize(EventArgs e)
121 | {
122 | UpdateText();
123 | }
124 |
125 | private void UpdateText()
126 | {
127 | _fontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
128 | _fontDrawing.DrawingPrimitives.Clear();
129 | _endGameTextSize = _font.Font.Measure(_endGameText);
130 |
131 | float fontOffset = 0;
132 | float endOffset = 0;
133 |
134 | if (_newHighScore)
135 | {
136 | fontOffset += _fontDrawing.Print(_font.Font, "New High Score", new Vector3(0, 2.0f * _endGameTextSize.Height, 0), QFontAlignment.Centre, Color.White).Height;
137 | fontOffset += _fontDrawing.Print(_font.Font, string.Format("Score: {0}", _highestScore.Score.ToString("N0", CultureInfo.CurrentCulture)), new Vector3(0, 0, 0), QFontAlignment.Centre, Color.White).Height;
138 | fontOffset += _fontDrawing.Print(_font.Font, string.Format("Accuracy: {0}%", _highestScore.Accuracy.ToString("#.##")), new Vector3(0, - _endGameTextSize.Height, 0), QFontAlignment.Centre, Color.White).Height;
139 | endOffset = -3.0f * _endGameTextSize.Height;
140 | }
141 | else
142 | {
143 | fontOffset += _fontDrawing.Print(_font.Font, string.Format("High Score: {0}", _highestScore.Score), new Vector3(0, 2.0f * _endGameTextSize.Height, 0), QFontAlignment.Centre, Color.White).Height;
144 | fontOffset += _fontDrawing.Print(_font.Font, string.Format("Score: {0}", _newScore.Score.ToString("N0", CultureInfo.CurrentCulture)), new Vector3(0, 0, 0), QFontAlignment.Centre, Color.White).Height;
145 | fontOffset += _fontDrawing.Print(_font.Font, string.Format("Accuracy: {0}%", _newScore.Accuracy.ToString("#.##")), new Vector3(0, -_endGameTextSize.Height, 0), QFontAlignment.Centre, Color.White).Height;
146 | endOffset = -3.0f * _endGameTextSize.Height;
147 | }
148 | _fontDrawing.Print(_font.Font, _endGameText, new Vector3(0, -(WindowHeight)/2.0f + _endGameTextSize.Height + 20, 0), QFontAlignment.Centre, Color.White);
149 | _fontDrawing.Print(_font.Font, string.Format("{0} - {1}", _stage.CurrentSong.SongBase.Identifier, _stage.CurrentDifficulty), new Vector3(0, (WindowHeight) / 2.0f - 20, 0),
150 | new SizeF(WindowWidth * 0.75f, -1), QFontAlignment.Centre, new QFontRenderOptions { Colour = Color.White });
151 | _fontDrawing.RefreshBuffers();
152 | }
153 |
154 | public override void Update(double time, bool focused = false)
155 | {
156 | if (InputSystem.NewKeys.Contains(Key.Enter) || InputSystem.NewKeys.Contains(Key.Escape) || InputSystem.NewKeys.Contains(Key.Space))
157 | {
158 | _stage.StageGeometry.Player.Reset();
159 | SceneManager.RemoveScene(this, true);
160 | }
161 | }
162 |
163 | public override void Draw(double time)
164 | {
165 | _fontDrawing.Draw();
166 | }
167 |
168 | public override void Dispose()
169 | {
170 | _stage.Dispose();
171 | _fontDrawing.Dispose();
172 | }
173 |
174 | public override void EnterFocus()
175 | {
176 | }
177 |
178 | public override void ExitFocus()
179 | {
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/LoadingScene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 | using TurntNinja.Audio;
11 | using TurntNinja.Core;
12 | using TurntNinja.Game;
13 | using OpenTK;
14 | using OpenTK.Graphics;
15 | using QuickFont;
16 | using QuickFont.Configuration;
17 | using Substructio.Core.Math;
18 | using Substructio.Graphics.OpenGL;
19 | using Substructio.GUI;
20 | using OpenTK.Graphics.OpenGL4;
21 | using Substructio.Core;
22 | using System.Diagnostics;
23 |
24 | namespace TurntNinja.GUI
25 | {
26 | class LoadingScene : Scene
27 | {
28 | private float _audioCorrection;
29 | private float _maxAudioVolume;
30 |
31 | private Task _loadTask;
32 | private Stage _stage;
33 | private Player _player;
34 | private PolarPolygon _centerPolygon;
35 | private ProcessedText _loadingText;
36 | private ProcessedText _songText;
37 | private Vector3 _loadingTextPosition;
38 | private GameFont _loadingFont;
39 | private QFontDrawing _loadingFontDrawing;
40 | private QFontRenderOptions _loadingFontRenderOptions;
41 | private ShaderProgram _shaderProgram;
42 | private bool usePlaylist = false;
43 | private List _files = new List();
44 |
45 | private Song _song;
46 |
47 | private string _loadingStatus = "";
48 |
49 | public LoadingScene(float audioCorrection, float maxAudioVolume, PolarPolygon centerPolygon, Player player, ShaderProgram shaderProgram, Song song)
50 | {
51 | Exclusive = true;
52 | _audioCorrection = audioCorrection;
53 | _maxAudioVolume = maxAudioVolume;
54 | _centerPolygon = centerPolygon;
55 | _player = player;
56 | _shaderProgram = shaderProgram;
57 |
58 | _song = song;
59 | }
60 |
61 | public override void Load()
62 | {
63 | SceneManager.GameWindow.Cursor = MouseCursor.Empty;
64 |
65 | _stage = new Stage(this.SceneManager);
66 | _stage.ShaderProgram = _shaderProgram;
67 |
68 | _loadingFontRenderOptions = new QFontRenderOptions();
69 | _loadingFontRenderOptions.DropShadowActive = true;
70 | _loadingFont = SceneManager.GameFontLibrary.GetFirstOrDefault(GameFontType.Heading);
71 | _loadingFontDrawing = new QFontDrawing();
72 | _loadingFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
73 | _loadingText = QFontDrawingPrimitive.ProcessText(_loadingFont.Font, _loadingFontRenderOptions, "Loading", new SizeF(200, -1), QFontAlignment.Centre);
74 | _loadingTextPosition = CalculateTextPosition(new Vector3((float)SceneManager.GameWindow.Width/ 2, SceneManager.GameWindow.Height/ 2, 0f), _loadingText);
75 |
76 | _songText = QFontDrawingPrimitive.ProcessText(_loadingFont.Font, _loadingFontRenderOptions, _song.SongBase.Identifier, new SizeF(SceneManager.GameWindow.Width - 40, -1), QFontAlignment.Centre);
77 |
78 | //Get difficulty options
79 | DifficultyOptions dOptions;
80 | switch ((DifficultyLevels)SceneManager.GameSettings["DifficultyLevel"])
81 | {
82 | case DifficultyLevels.Easy:
83 | dOptions = DifficultyOptions.Easy;
84 | break;
85 | case DifficultyLevels.Medium:
86 | dOptions = DifficultyOptions.Medium;
87 | break;
88 | case DifficultyLevels.Hard:
89 | dOptions = DifficultyOptions.Hard;
90 | break;
91 | case DifficultyLevels.Ultra:
92 | dOptions = DifficultyOptions.Ultra;
93 | break;
94 | case DifficultyLevels.Ninja:
95 | dOptions = DifficultyOptions.Ninja;
96 | break;
97 | default:
98 | //shouldn't happen
99 | throw new Exception("Invalid difficulty level specified");
100 | }
101 |
102 | var progress = new Progress(status =>
103 | {
104 | _loadingStatus = status;
105 | });
106 | _loadTask = Task.Factory.StartNew(() => _stage.LoadAsync(_song, _audioCorrection, _maxAudioVolume, progress, _centerPolygon, _player, dOptions, (DifficultyLevels)SceneManager.GameSettings["DifficultyLevel"]));
107 |
108 | Loaded = true;
109 | }
110 |
111 | public override void CallBack(GUICallbackEventArgs e)
112 | {
113 | throw new NotImplementedException();
114 | }
115 |
116 | public override void Resize(EventArgs e)
117 | {
118 | _loadingText = QFontDrawingPrimitive.ProcessText(_loadingFont.Font, _loadingFontRenderOptions, "Loading", new SizeF(1000, -1), QFontAlignment.Centre);
119 | _loadingFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.ScreenProjectionMatrix;
120 | _loadingTextPosition = CalculateTextPosition(new Vector3(SceneManager.ScreenCamera.PreferredWidth / 2, SceneManager.ScreenCamera.PreferredHeight / 2, 0f), _loadingText);
121 | }
122 |
123 | public override void Update(double time, bool focused = false)
124 | {
125 | if (_loadTask.Exception != null)
126 | {
127 | Trace.WriteLine(_loadTask.Exception.Message);
128 | throw _loadTask.Exception;
129 | }
130 | if (_loadTask.IsCompleted)
131 | {
132 | SceneManager.RemoveScene(this);
133 | SceneManager.AddScene(new GameScene(_stage){ShaderProgram = _shaderProgram, UsingPlaylist = usePlaylist, PlaylistFiles = _files}, this);
134 | }
135 |
136 | _player.Update(time);
137 | _centerPolygon.Update(time, false);
138 | //SceneManager.ScreenCamera.TargetScale += new Vector2(0.1f, 0.1f);
139 | }
140 |
141 | public override void Draw(double time)
142 | {
143 | GL.Disable(EnableCap.CullFace);
144 | _shaderProgram.Bind();
145 | _shaderProgram.SetUniform("mvp", SceneManager.ScreenCamera.WorldModelViewProjection);
146 | _shaderProgram.SetUniform("in_color", Color4.White);
147 |
148 | //Draw the player
149 | _player.Draw(time);
150 |
151 | //Draw the center polygon
152 | _centerPolygon.Draw(time);
153 |
154 | _shaderProgram.SetUniform("in_color", Color4.Black);
155 | _centerPolygon.DrawOutline(time);
156 |
157 | //Cleanup the program
158 | _shaderProgram.UnBind();
159 |
160 | _loadingFontDrawing.DrawingPrimitives.Clear();
161 | float yOffset = 0;
162 | yOffset += _loadingFontDrawing.Print(_loadingFont.Font, _loadingText, _loadingTextPosition).Height;
163 | yOffset = MathHelper.Clamp(yOffset + 200 - 50*SceneManager.ScreenCamera.Scale.Y, yOffset, SceneManager.GameWindow.Height*0.5f);
164 | var pos = new Vector3(0, -yOffset, 0);
165 | yOffset += _loadingFontDrawing.Print(_loadingFont.Font, _songText, pos).Height;
166 | yOffset += _loadingFontDrawing.Print(_loadingFont.Font, _loadingStatus, new Vector3(0, -yOffset, 0), QFontAlignment.Centre).Height;
167 | _loadingFontDrawing.RefreshBuffers();
168 | _loadingFontDrawing.Draw();
169 | }
170 |
171 | public override void Dispose()
172 | {
173 | if (_loadingFontDrawing != null)
174 | {
175 | _loadingFontDrawing.Dispose();
176 | }
177 | }
178 |
179 | private Vector3 CalculateTextPosition(Vector3 center, ProcessedText text)
180 | {
181 | var size = _loadingFont.Font.Measure(text);
182 | return new Vector3(0, size.Height/2, 0f);
183 | }
184 |
185 | public override void EnterFocus()
186 | {
187 | }
188 |
189 | public override void ExitFocus()
190 | {
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/TurntNinja/FileSystem/LocalFileSystem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using TurntNinja.Audio;
9 | using System.Diagnostics;
10 | using Substructio.Core;
11 |
12 | namespace TurntNinja.FileSystem
13 | {
14 | class LocalFileSystem : IFileSystem
15 | {
16 | string[] _fileFilter;
17 | FileBrowserEntry _entrySeparator;
18 | List _drives;
19 | List _localFileSystemEntries;
20 | List _userFolders;
21 | IDirectoryHandler _directoryHandler;
22 |
23 | public ReadOnlyCollection FileSystemEntryCollection { get { return _localFileSystemEntries.AsReadOnly(); } }
24 | public List FileSystemCollection { get; set; }
25 | public string FriendlyName { get { return "Local Songs"; } }
26 |
27 | public LocalFileSystem(IDirectoryHandler directoryHandler)
28 | {
29 | _fileFilter = CSCore.Codecs.CodecFactory.Instance.GetSupportedFileExtensions();
30 | _directoryHandler = directoryHandler;
31 |
32 | _drives = new List();
33 | _localFileSystemEntries = new List();
34 | _userFolders = new List();
35 | }
36 |
37 | public int Initialise(FileBrowserEntry separator)
38 | {
39 | _entrySeparator = separator;
40 |
41 | // populate the drive list
42 | _drives.Clear();
43 | _localFileSystemEntries.Clear();
44 | _userFolders.Clear();
45 | foreach (var driveInfo in DriveInfo.GetDrives().Where(d => d.IsReady))
46 | {
47 | _drives.Add(new FileBrowserEntry
48 | {
49 | Path = driveInfo.RootDirectory.FullName,
50 | EntryType = FileBrowserEntryType.Directory | FileBrowserEntryType.Drive | FileBrowserEntryType.Special,
51 | Name = string.IsNullOrWhiteSpace(driveInfo.VolumeLabel) ? driveInfo.Name : string.Format("{1} ({0})", driveInfo.Name, driveInfo.VolumeLabel)
52 | });
53 | }
54 |
55 | return EnterPath(_drives.First().Path);
56 | }
57 |
58 | public bool EntrySelected(ref int entryIndex)
59 | {
60 | var entry = _localFileSystemEntries[entryIndex];
61 |
62 | // If a song has been selected, return immediately and notify the caller that the song is ready to be loaded
63 | if (entry.EntryType.HasFlag(FileBrowserEntryType.Song) && File.Exists(entry.Path)) return true;
64 | entryIndex = EnterPath(entry.Path);
65 |
66 | return false;
67 | }
68 |
69 | private int EnterPath(string directoryPath)
70 | {
71 | // Sanity check that directory exists
72 | if (!Directory.Exists(directoryPath)) throw new Exception("Directory doesn't exist: " + directoryPath);
73 |
74 | // Get a list of children in new directory
75 | var childrenDirectories = Directory.EnumerateDirectories(directoryPath).Where(d => !new DirectoryInfo(d).Attributes.HasFlag(FileAttributes.Hidden));
76 | var childrenFiles = Directory.EnumerateFiles(directoryPath).Where(p => _fileFilter.Any(f => p.EndsWith(f, StringComparison.OrdinalIgnoreCase)));
77 |
78 | // Clear the file system entry list
79 | _localFileSystemEntries.Clear();
80 |
81 | // Add the drive list
82 | _localFileSystemEntries.AddRange(_drives);
83 |
84 | // Add Drive/Directory separator
85 | _localFileSystemEntries.Add(_entrySeparator);
86 |
87 | // Add user folders
88 | _localFileSystemEntries.Add(new FileBrowserEntry
89 | {
90 | Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
91 | EntryType = FileBrowserEntryType.Directory,
92 | Name = "My Documents"
93 | });
94 | _localFileSystemEntries.Add(new FileBrowserEntry
95 | {
96 | Path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
97 | EntryType = FileBrowserEntryType.Directory,
98 | Name = "Desktop"
99 | });
100 | _localFileSystemEntries.Add(new FileBrowserEntry
101 | {
102 | Path = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic),
103 | EntryType = FileBrowserEntryType.Directory,
104 | Name = "My Music"
105 | });
106 |
107 | _localFileSystemEntries.Add(new FileBrowserEntry
108 | {
109 | Path = _directoryHandler["BundledSongs"].FullName,
110 | EntryType = FileBrowserEntryType.Directory,
111 | Name = "Bundled Songs"
112 | });
113 |
114 | _localFileSystemEntries.Add(_entrySeparator);
115 |
116 | // Add shortcut to parent directory
117 | _localFileSystemEntries.Add(new FileBrowserEntry
118 | {
119 | Path = Path.Combine(directoryPath, "../"),
120 | EntryType = FileBrowserEntryType.Directory | FileBrowserEntryType.Special,
121 | Name = "Back"
122 | });
123 |
124 | _localFileSystemEntries.Add(_entrySeparator);
125 |
126 | int desiredIndex = _localFileSystemEntries.Count;
127 |
128 | // Add the new directories
129 | foreach (var dir in childrenDirectories.OrderBy(Path.GetFileName))
130 | {
131 | _localFileSystemEntries.Add(new FileBrowserEntry
132 | {
133 | Path = dir,
134 | Name = Path.GetFileName(dir),
135 | EntryType = FileBrowserEntryType.Directory
136 | });
137 | }
138 |
139 | // Add Directory/File separator
140 | _localFileSystemEntries.Add(_entrySeparator);
141 |
142 | // Add the new files
143 | foreach (var file in childrenFiles.OrderBy(Path.GetFileName))
144 | {
145 | _localFileSystemEntries.Add(new FileBrowserEntry
146 | {
147 | Path = file,
148 | Name = Path.GetFileNameWithoutExtension(file),
149 | EntryType = FileBrowserEntryType.Song
150 | });
151 | }
152 |
153 | // Update the index position
154 | if (_localFileSystemEntries.Count <= desiredIndex) desiredIndex = _localFileSystemEntries.Count - 1;
155 |
156 | return desiredIndex;
157 | }
158 |
159 | public Song LoadSongInformation(int entryIndex)
160 | {
161 | var entry = _localFileSystemEntries[entryIndex];
162 | TagLib.Tag tag;
163 |
164 | // Load the tag information
165 | string artist = "";
166 | string title = "";
167 |
168 | if (TagLib.SupportedMimeType.AllExtensions.Any(s => entry.Path.EndsWith(s, StringComparison.OrdinalIgnoreCase)))
169 | {
170 | using (var fs = File.Open(entry.Path, FileMode.Open, FileAccess.Read, FileShare.Read))
171 | {
172 | try
173 | {
174 | var file = TagLib.File.Create(new TagLib.StreamFileAbstraction(entry.Name, fs, fs));
175 | tag = file.Tag;
176 | artist = tag.AlbumArtists.Count() > 0 ? tag.AlbumArtists[0] : "";
177 | title = tag.Title;
178 | file.Dispose();
179 | }
180 | catch (TagLib.UnsupportedFormatException ex)
181 | {
182 | }
183 | }
184 | }
185 |
186 | title = string.IsNullOrWhiteSpace(title) ? Path.GetFileNameWithoutExtension(entry.Path) : title;
187 | artist = string.IsNullOrWhiteSpace(title) ? "Unkown Artist" : artist;
188 |
189 | // Initialise the song variable
190 | var ret = new Song
191 | {
192 | FileSystem = this,
193 | SongBase = new SongBase
194 | {
195 | InternalName = entry.Path,
196 | Artist = artist,
197 | Identifier = entry.Name,
198 | TrackName = title,
199 | FileSystemFriendlyName = FriendlyName,
200 | }
201 | };
202 |
203 | // Song information is loaded so we return it
204 | return ret;
205 | }
206 |
207 | public void LoadSongAudio(Song song)
208 | {
209 | // Sanity checks
210 | if (!File.Exists(song.SongBase.InternalName)) throw new Exception("File not found: " + song.SongBase.InternalName);
211 |
212 | song.SongAudio = CSCore.Codecs.CodecFactory.Instance.GetCodec(song.SongBase.InternalName);
213 | song.SongAudioLoaded = true;
214 | }
215 |
216 | public bool SongExists(SongBase song)
217 | {
218 | return File.Exists(song.InternalName);
219 | }
220 |
221 | public void Focused()
222 | {
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/TurntNinja/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Reflection;
5 | using TurntNinja.Core.Settings;
6 | using TurntNinja.Logging;
7 | using OpenTK;
8 | using OpenTK.Graphics;
9 | using Substructio.Core;
10 | using Substructio.IO;
11 | using System.Threading.Tasks;
12 | using System.Collections.Generic;
13 |
14 | namespace TurntNinja
15 | {
16 | public static class TurntNinjaGame
17 | {
18 | private static CrashReporter _crashReporter;
19 |
20 | [STAThread]
21 | public static void Main(string[] args)
22 | {
23 | System.Net.ServicePointManager.ServerCertificateValidationCallback = (s, ce, ch, p) => true;
24 |
25 | // Load services
26 | var initialSettingsProvider = new PropertySettings();
27 | initialSettingsProvider.Load();
28 |
29 | // Default settings provider is the Windows application settings implementation
30 | ServiceLocator.Settings = initialSettingsProvider;
31 |
32 | // DEBUG SETTINGS - Force first run
33 | //ServiceLocator.Settings["Analytics"] = false;
34 | //ServiceLocator.Settings["FirstRun"] = true;
35 |
36 | string PiwikURL = AESEncryption.Decrypt((string)ServiceLocator.Settings["Piwik"]);
37 | string SentryURL = AESEncryption.Decrypt((string)ServiceLocator.Settings["Sentry"]);
38 |
39 | #if DEBUG
40 | string sentryEnvironment = "debug";
41 | #else
42 | string sentryEnvironment = "release";
43 | #endif
44 | Version version = Assembly.GetExecutingAssembly().GetName().Version;
45 | string gameVersion = $"{version.Major}.{version.Minor}.{version.Build}";
46 | var userID = (string)ServiceLocator.Settings["UserID"];
47 | // Generate or load user ID for Piwik
48 | Guid userGUID;
49 | if (!Guid.TryParse(userID, out userGUID)) userGUID = Guid.NewGuid();
50 |
51 | // Save user GUID
52 | ServiceLocator.Settings["UserID"] = userGUID.ToString();
53 |
54 | Platform runningPlatform = PlatformDetection.RunningPlatform();
55 | string platformVersion = PlatformDetection.GetVersionName();
56 |
57 | // Load Sentry service
58 | if ((bool)ServiceLocator.Settings["Analytics"] || (bool)ServiceLocator.Settings["FirstRun"])
59 | {
60 | ServiceLocator.ErrorReporting = new SentryErrorReporting(
61 | SentryURL,
62 | sentryEnvironment,
63 | gameVersion,
64 | userGUID.ToString(),
65 | runningPlatform,
66 | platformVersion);
67 | }
68 |
69 | int PiwikAppID = 3;
70 |
71 | // Load Piwik service
72 | if ((bool)ServiceLocator.Settings["Analytics"] || (bool)ServiceLocator.Settings["FirstRun"])
73 | {
74 | ServiceLocator.Analytics = new PiwikAnalytics(
75 | PiwikAppID,
76 | PiwikURL,
77 | runningPlatform,
78 | platformVersion,
79 | DisplayDevice.Default.Width,
80 | DisplayDevice.Default.Height,
81 | gameVersion,
82 | userGUID.ToString(),
83 | "http://turntninja");
84 | }
85 |
86 | ServiceLocator.Directories = new DirectoryHandler();
87 |
88 | var directoryHandler = ServiceLocator.Directories;
89 |
90 | //set application path
91 | directoryHandler.AddPath("Application", AppDomain.CurrentDomain.BaseDirectory);
92 | //set base path
93 | if (Directory.Exists(directoryHandler.Locate("Application", "Content")))
94 | directoryHandler.AddPath("Base", directoryHandler["Application"].FullName);
95 | else if (Directory.Exists(directoryHandler.Locate("Application", @"../../src/TurntNinja/Content")))
96 | directoryHandler.AddPath("Base", directoryHandler.Locate("Application", @"../../src/TurntNinja"));
97 | else
98 | {
99 | throw new Exception("Couldn't find resource folder location");
100 | }
101 |
102 | directoryHandler.AddPath("AppData",
103 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), (string)ServiceLocator.Settings["AppDataFolderName"]));
104 |
105 | directoryHandler.AddPath("Resources", Path.Combine(directoryHandler["Base"].FullName, "Content"));
106 | directoryHandler.AddPath("Fonts", Path.Combine(directoryHandler["Resources"].FullName, @"Fonts"));
107 | directoryHandler.AddPath("Shaders", Path.Combine(directoryHandler["Resources"].FullName, @"Shaders"));
108 | directoryHandler.AddPath("Images", Path.Combine(directoryHandler["Resources"].FullName, @"Images"));
109 | directoryHandler.AddPath("Crash", Path.Combine(directoryHandler["AppData"].FullName, "CrashLogs"));
110 | directoryHandler.AddPath("BundledSongs", Path.Combine(directoryHandler["Resources"].FullName, @"Songs"));
111 | directoryHandler.AddPath("ProcessedSongs", Path.Combine(directoryHandler["AppData"].FullName, @"Processed Songs"));
112 |
113 | if (!Directory.Exists(directoryHandler["AppData"].FullName)) Directory.CreateDirectory(directoryHandler["AppData"].FullName);
114 |
115 | if (!Debugger.IsAttached)
116 | {
117 | //initialise crash reporter
118 | _crashReporter = new CrashReporter(directoryHandler["Crash"].FullName);
119 |
120 | //attach exception handlers
121 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
122 | }
123 |
124 | // If we are not running on a windows platform, use the JsonSettings backend
125 | if (PlatformDetection.RunningPlatform() != Platform.Windows)
126 | {
127 | ServiceLocator.Settings = new JsonSettings(initialSettingsProvider, directoryHandler.Locate("AppData", "settings.json"));
128 | ServiceLocator.Settings.Load();
129 | }
130 |
131 | //init logging
132 | Splat.DependencyResolverMixins.RegisterConstant(Splat.Locator.CurrentMutable,
133 | new SimpleLogger(directoryHandler["Application"].FullName, Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".log")
134 | {
135 | Level = Splat.LogLevel.Debug
136 | },
137 | typeof(Splat.ILogger));
138 |
139 | int rX = (int)ServiceLocator.Settings["ResolutionX"];
140 | int rY = (int)ServiceLocator.Settings["ResolutionY"];
141 | int FSAASamples = (int)ServiceLocator.Settings["AntiAliasingSamples"];
142 | GraphicsMode graphicsMode = new GraphicsMode(32, 32, 8, FSAASamples, GraphicsMode.Default.AccumulatorFormat, 3);
143 |
144 | // Choose right OpenGL version for mac
145 | int major = 3;
146 | int minor = 0;
147 | if (PlatformDetection.RunningPlatform() == Platform.MacOSX)
148 | major = 4;
149 |
150 | if ((bool)ServiceLocator.Settings["Analytics"])
151 | ServiceLocator.Analytics.TrackApplicationStartup();
152 |
153 | using (GameController game = new GameController(ServiceLocator.Settings, rX, rY, graphicsMode, "Turnt Ninja",
154 | major, minor, directoryHandler))
155 | {
156 | game.Title = "Turnt Ninja";
157 | // Only set icon if we're on Windows or Linux
158 | if (runningPlatform != Platform.MacOSX)
159 | game.Icon = new System.Drawing.Icon(directoryHandler.Locate("Images", "icon.ico"));
160 | game.Run();
161 | }
162 | }
163 |
164 | private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
165 | {
166 | try
167 | {
168 | Exception ex = (Exception)e.ExceptionObject;
169 |
170 | // Try to report exception
171 | try
172 | {
173 | string eventID = ServiceLocator.ErrorReporting.ReportError(ex);
174 |
175 | // Try to log crash ID
176 | try
177 | {
178 | ServiceLocator.Analytics.TrackEvent("Crash", "Fatal Crash", eventID);
179 | }
180 | catch { }
181 | }
182 | catch { }
183 |
184 | // Try to save crash report
185 | try
186 | {
187 | _crashReporter.LogError(ex);
188 | }
189 | catch { }
190 |
191 | // Try to report application shutdown
192 | try
193 | {
194 | ServiceLocator.Analytics.TrackApplicationShutdown();
195 | }
196 | catch { }
197 | }
198 | finally
199 | {
200 | Environment.Exit(-1);
201 | }
202 | }
203 |
204 | public static string AssemblyDirectory
205 | {
206 | get
207 | {
208 | string codeBase = Assembly.GetExecutingAssembly().CodeBase;
209 | UriBuilder uri = new UriBuilder(codeBase);
210 | string path = Uri.UnescapeDataString(uri.Path);
211 | return Path.GetDirectoryName(path);
212 | }
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/src/TurntNinja/Game/StageAudio.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using CSCore;
8 | using CSCore.Codecs;
9 | using CSCore.CoreAudioAPI;
10 | using CSCore.SoundOut;
11 | using CSCore.Streams;
12 | using OpenTK;
13 | using Substructio.Core;
14 |
15 |
16 | namespace TurntNinja.Game
17 | {
18 | class StageAudio : IDisposable
19 | {
20 | public int AudioHashCode { get; private set; }
21 | private const int HashCount = 10000;
22 | //private WaveOut _waveOut;
23 | //private WaveStream _waveProvider;
24 | private float _maxVolume;
25 |
26 | private CancellationTokenSource _tokenSource = new CancellationTokenSource();
27 |
28 | private IAudio _audio;
29 |
30 | ///
31 | /// Volume is clamped between 0 and MaxVolume
32 | ///
33 | public float Volume
34 | {
35 | get { return _audio.Volume; }
36 | set { _audio.Volume = MathHelper.Clamp(value, 0, 1); }
37 | }
38 |
39 | ///
40 | /// Max volume is between 0 and 1
41 | ///
42 | public float MaxVolume
43 | {
44 | get { return _maxVolume; }
45 | set { _maxVolume = MathHelper.Clamp(value, 0, 1); }
46 | }
47 |
48 | public bool IsStopped
49 | {
50 | get { return _audio.PlaybackState == PlaybackState.Stopped; }
51 | }
52 |
53 | public IWaveSource Source
54 | {
55 | get { return _audio.GetSource(); }
56 | }
57 |
58 | public void Load(IWaveSource source)
59 | {
60 | _audio = new CSCoreAudio();
61 | _audio.Init(source);
62 | AudioHashCode = CRC16.Instance().ComputeChecksum(_audio.GetHashBytes(HashCount));
63 | }
64 |
65 | public void Play()
66 | {
67 | _audio.Play();
68 | }
69 |
70 | public void Pause()
71 | {
72 | _audio.Pause();
73 | }
74 |
75 | public void Stop()
76 | {
77 | _audio.Stop();
78 | _audio.Seek(0);
79 | }
80 |
81 | public void Resume()
82 | {
83 | _audio.Resume();
84 | }
85 |
86 | public void Seek(float percent)
87 | {
88 | _audio.Seek(percent);
89 | }
90 |
91 | public string CreateTempWavFile(string audioFilePath, string tempFolderName = "")
92 | {
93 | var newFile = Path.Combine(Path.GetTempPath() + tempFolderName, Path.GetFileNameWithoutExtension(audioFilePath)) + ".wav";
94 |
95 | //create directory if it doesn't exist
96 | var dir = Path.GetDirectoryName(newFile);
97 | if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
98 |
99 | _audio.ConvertToWav(newFile);
100 | return newFile;
101 | }
102 |
103 | ///
104 | /// Fades out the audio.
105 | ///
106 | ///
107 | ///
108 | ///
109 | /// What action to do at the end of the fade
110 | public Task FadeOut(float time, float minVolume, float dVolume, FadeEndAction fadeEndAction)
111 | {
112 | var ct = _tokenSource.Token;
113 | return Task.Run(async () =>
114 | {
115 | if (this.IsStopped) return;
116 | int dt = (int)(time / ((Volume - minVolume) / dVolume));
117 | while (Volume > minVolume && !ct.IsCancellationRequested)
118 | {
119 | Volume -= dVolume;
120 | await Task.Delay(dt, ct);
121 | }
122 | switch (fadeEndAction)
123 | {
124 | case FadeEndAction.Pause:
125 | Pause();
126 | break;
127 | case FadeEndAction.Stop:
128 | Stop();
129 | break;
130 | }
131 | }, ct);
132 | }
133 |
134 | ///
135 | /// Fades in the audio
136 | ///
137 | /// Time over which to fade the audio in
138 | /// The maximum volume to reach
139 | /// The volume step size to use
140 | /// What action to do at the end of the fade
141 | public Task FadeIn(float time, float maxVolume, float dVolume, FadeEndAction fadeEndAction)
142 | {
143 | if (maxVolume > MaxVolume) throw new ArgumentOutOfRangeException("The maximum fade in volume " + maxVolume + " was greater than the maximum volume for this audio " + MaxVolume);
144 | var ct = _tokenSource.Token;
145 | return Task.Run(async () =>
146 | {
147 | int dt = (int)(time / ((maxVolume - Volume) / dVolume));
148 | while (Volume < maxVolume && !ct.IsCancellationRequested)
149 | {
150 | Volume += dVolume;
151 | await Task.Delay(dt, ct);
152 | }
153 | switch (fadeEndAction)
154 | {
155 | case FadeEndAction.Pause:
156 | Pause();
157 | break;
158 | case FadeEndAction.Stop:
159 | Stop();
160 | break;
161 | }
162 | }, ct);
163 | }
164 |
165 | public void CancelAudioFades()
166 | {
167 | _tokenSource.Cancel();
168 | _tokenSource = new CancellationTokenSource();
169 | }
170 |
171 | public void Dispose()
172 | {
173 | if (_audio != null) _audio.Dispose();
174 | }
175 | }
176 |
177 |
178 | internal interface IAudio : IDisposable
179 | {
180 | PlaybackState PlaybackState { get; }
181 | float Volume { get; set; }
182 | //void Init(string audioFilePath);
183 | void Init(IWaveSource source);
184 | byte[] GetHashBytes(int hashByteCount);
185 | void Play();
186 | void Pause();
187 | void Resume();
188 | void Stop();
189 | void Seek(float percent);
190 | void ConvertToWav(string wavFilePath);
191 | IWaveSource GetSource();
192 | }
193 |
194 | enum FadeEndAction
195 | {
196 | Nothing,
197 | Pause,
198 | Stop,
199 | }
200 |
201 | enum PlaybackState
202 | {
203 | Paused,
204 | Playing,
205 | Stopped
206 | }
207 |
208 | class CSCoreAudio : IAudio
209 | {
210 | private ISoundOut _soundOut;
211 | private IWaveSource _soundSource;
212 |
213 | public void Dispose()
214 | {
215 | if (_soundOut != null)
216 | {
217 | _soundOut.Stop();
218 | _soundOut.Dispose();
219 | if (_soundOut is ALSoundOut)
220 | {
221 | CSCore.SoundOut.AL.ALDevice.DefaultDevice.Dispose();
222 | }
223 | _soundOut = null;
224 | }
225 | if (_soundSource != null)
226 | {
227 | _soundSource.Dispose();
228 | _soundSource = null;
229 | }
230 | }
231 |
232 | public PlaybackState PlaybackState
233 | {
234 | get
235 | {
236 | switch (_soundOut.PlaybackState)
237 | {
238 | case CSCore.SoundOut.PlaybackState.Paused:
239 | return PlaybackState.Paused;
240 | case CSCore.SoundOut.PlaybackState.Playing:
241 | return PlaybackState.Playing;
242 | case CSCore.SoundOut.PlaybackState.Stopped:
243 | return PlaybackState.Stopped;
244 | }
245 | return PlaybackState.Stopped;
246 | }
247 | }
248 |
249 | public float Volume { get { return _soundOut.Volume; } set { _soundOut.Volume = value; } }
250 |
251 | public IWaveSource GetSource()
252 | {
253 | return _soundSource;
254 | }
255 |
256 | //public void Init(string audioFilePath)
257 | //{
258 | // Init(CodecFactory.Instance.GetCodec(audioFilePath));
259 | //}
260 |
261 | public void Init(IWaveSource source)
262 | {
263 | _soundSource = source;
264 | // Use ALSound out if we are running on Mac/Linux
265 | _soundOut = (PlatformDetection.RunningPlatform() == Platform.Windows) ?
266 | (ISoundOut) new WasapiOut() : new ALSoundOut();
267 | _soundOut.Initialize(_soundSource);
268 | _soundOut.Stopped += (sender, args) => { if (args.HasError) throw new Exception("exception thrown on stoping audio", args.Exception); };
269 | }
270 |
271 | public byte[] GetHashBytes(int hashByteCount)
272 | {
273 | var ret = new byte[hashByteCount];
274 | _soundSource.Read(ret, 0, hashByteCount);
275 | _soundSource.Position = 0;
276 | return ret;
277 | }
278 |
279 |
280 | public void Play()
281 | {
282 | _soundOut.Play();
283 | }
284 |
285 | public void Resume()
286 | {
287 | _soundOut.Resume();
288 | }
289 |
290 | public void Stop()
291 | {
292 | _soundOut.Stop();
293 | _soundSource.SetPosition(TimeSpan.Zero);
294 | }
295 |
296 | public void Seek(float percent)
297 | {
298 | _soundSource.SetPosition(TimeSpan.FromMilliseconds(percent * _soundSource.GetLength().TotalMilliseconds));
299 | }
300 |
301 | public void ConvertToWav(string wavFilePath)
302 | {
303 | _soundSource.WriteToFile(wavFilePath);
304 | }
305 |
306 | public void Pause()
307 | {
308 | _soundOut.Pause();
309 | }
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/TurntNinja/GUI/MenuScene.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using TurntNinja.Core;
9 | using Gwen.Skin;
10 | using OpenTK;
11 | using OpenTK.Graphics;
12 | using OpenTK.Input;
13 | using QuickFont;
14 | using QuickFont.Configuration;
15 | using Substructio.Core;
16 | using Substructio.Core.Math;
17 | using Substructio.Graphics.OpenGL;
18 | using Substructio.GUI;
19 |
20 | namespace TurntNinja.GUI
21 | {
22 | class MenuScene : Scene
23 | {
24 | private ShaderProgram _shaderProgram;
25 | private Player _player;
26 |
27 | private PolarPolygon _centerPolygon;
28 |
29 | private string _selectedMenuItemText = "";
30 | private MainMenuOptions _selectedMenuItem = MainMenuOptions.None;
31 | private bool _selectedItemChanged;
32 |
33 | private GameFont _menuFont;
34 | private GameFont _versionFont;
35 | private QFontDrawing _menuFontDrawing;
36 | private QFontRenderOptions _menuRenderOptions;
37 | private QFontDrawingPrimitive _menuFDP;
38 |
39 | private GUIComponentContainer _GUIComponents;
40 |
41 | private double _totalTime;
42 |
43 | private string _gameVersion;
44 |
45 | public override void Load()
46 | {
47 | SceneManager.GameWindow.Cursor = MouseCursor.Default;
48 |
49 | // Remap keypad enter to normal enter
50 | InputSystem.KeyRemappings.Add(Key.KeypadEnter, Key.Enter);
51 |
52 | _gameVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
53 |
54 | // Choose correct version directive because OSX is dumb
55 | string version = "#version 130";
56 | if (PlatformDetection.RunningPlatform() == Platform.MacOSX)
57 | version = "#version 150";
58 |
59 | // Load shaders
60 | var vert = new Shader(Path.Combine(SceneManager.Directories["Shaders"].FullName, "simple.vs"), version);
61 | var frag = new Shader(Path.Combine(SceneManager.Directories["Shaders"].FullName, "simple.fs"), version);
62 | _shaderProgram = new ShaderProgram();
63 | _shaderProgram.Load(vert, frag);
64 |
65 | _player = new Player();
66 | _player.Position = new PolarVector(1.5 * (Math.PI / 3) - _player.Length * 0.5f, _player.Position.Radius);
67 | _player.ShaderProgram = _shaderProgram;
68 | _centerPolygon = new PolarPolygon(Enumerable.Repeat(true, 6).ToList(), new PolarVector(0.5, 0), 50, 80, 0);
69 | _centerPolygon.ShaderProgram = _shaderProgram;
70 |
71 | _menuFont = SceneManager.GameFontLibrary.GetFirstOrDefault("menuworld");
72 | _menuFontDrawing = new QFontDrawing();
73 | _menuFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.WorldModelViewProjection;
74 | _menuRenderOptions = new QFontRenderOptions { DropShadowActive = true, Colour = Color.White };
75 |
76 | _versionFont = SceneManager.GameFontLibrary.GetFirstOrDefault("versiontext");
77 |
78 | var guiRenderer = new Gwen.Renderer.OpenTK();
79 | var skin = new TexturedBase(guiRenderer, Path.Combine(SceneManager.Directories["Images"].FullName, "DefaultSkin.png"));
80 | skin.DefaultFont = new Gwen.Font(guiRenderer, SceneManager.FontPath, 30);
81 | _GUIComponents = new GUIComponentContainer(guiRenderer, skin);
82 |
83 | Loaded = true;
84 | }
85 |
86 | public override void CallBack(GUICallbackEventArgs e)
87 | {
88 | }
89 |
90 | public override void Resize(EventArgs e)
91 | {
92 | _GUIComponents.Resize(SceneManager.ScreenCamera.ScreenProjectionMatrix, WindowWidth, WindowHeight);
93 | _menuFontDrawing.ProjectionMatrix = SceneManager.ScreenCamera.WorldModelViewProjection;
94 | _selectedItemChanged = true;
95 | }
96 |
97 | public override void Update(double time, bool focused = false)
98 | {
99 | // Update total elapsed time
100 | _totalTime += time;
101 |
102 | if (InputSystem.NewKeys.Contains(Key.Escape)) Exit();
103 |
104 | _player.Update(time);
105 | _centerPolygon.Update(time, false);
106 |
107 | DoGUI();
108 |
109 | // Update next if needed
110 | if (_selectedItemChanged)
111 | {
112 | // Reset elapsed time
113 | _totalTime = 0;
114 |
115 | _menuFontDrawing.DrawingPrimitives.Clear();
116 | _menuFDP = new QFontDrawingPrimitive(_menuFont.Font, _menuRenderOptions);
117 |
118 |
119 | _menuFDP.Print(_selectedMenuItemText.ToUpper(), Vector3.Zero, QFontAlignment.Centre);
120 | _menuFontDrawing.DrawingPrimitives.Add(_menuFDP);
121 | _selectedItemChanged = false;
122 | }
123 |
124 | // Pulse text
125 | var size = _menuFont.Font.Measure(_selectedMenuItemText.ToUpper());
126 | var selectedSide = GetSelectedSide();
127 | var newPos = new PolarVector(selectedSide * _centerPolygon.AngleBetweenSides + _centerPolygon.AngleBetweenSides * 0.5f, _player.Position.Radius + _player.Width + size.Height*0.9);
128 |
129 | var extraRotation = (selectedSide >= 0 && selectedSide < 3) ? (-Math.PI / 2.0) : (Math.PI / 2.0);
130 | var extraOffset = (selectedSide >= 0 && selectedSide < 3) ? (0) : (-size.Height / 4);
131 |
132 | newPos.Radius += extraOffset;
133 | var cart = newPos.ToCartesianCoordinates();
134 | var mvm = Matrix4.CreateTranslation(0, size.Height / 2, 0)
135 | * Matrix4.CreateScale(0.90f + (float)Math.Pow(Math.Sin(_totalTime*3), 2)*0.10f)
136 | * Matrix4.CreateRotationZ((float)(newPos.Azimuth + extraRotation))
137 | * Matrix4.CreateTranslation(cart.X, cart.Y, 0);
138 | _menuFDP.ModelViewMatrix = mvm;
139 | }
140 |
141 | public void Exit()
142 | {
143 | SceneManager.Exit();
144 | }
145 |
146 | private int GetSelectedSide()
147 | {
148 | var nTheta = MathUtilities.Normalise(_player.Position.Azimuth + _player.Length * 0.5f);
149 | return (int)Math.Floor(nTheta / _centerPolygon.AngleBetweenSides);
150 | }
151 |
152 | private void DoGUI()
153 | {
154 | int selectedSide = GetSelectedSide();
155 | if (_selectedMenuItem != (MainMenuOptions) selectedSide) _selectedItemChanged = true;
156 | _selectedMenuItem = (MainMenuOptions) selectedSide;
157 |
158 | switch (_selectedMenuItem)
159 | {
160 | case MainMenuOptions.SinglePlayer:
161 | _selectedMenuItemText = "Play";
162 | break;
163 | case MainMenuOptions.Scores:
164 | _selectedMenuItemText = "Scores";
165 | break;
166 | case MainMenuOptions.Options:
167 | _selectedMenuItemText = "Options";
168 | break;
169 | case MainMenuOptions.Exit:
170 | _selectedMenuItemText = "Exit";
171 | break;
172 | case MainMenuOptions.Update:
173 | _selectedMenuItemText = "Update";
174 | break;
175 | case MainMenuOptions.ComingSoon:
176 | _selectedMenuItemText = "Coming Soon";
177 | break;
178 | default:
179 | _selectedMenuItem = MainMenuOptions.None;
180 | _selectedMenuItemText = "";
181 | break;
182 | }
183 |
184 | // we have selected the current menu item
185 | if (InputSystem.NewKeys.Contains(Key.Enter))
186 | {
187 | switch (_selectedMenuItem)
188 | {
189 | case MainMenuOptions.SinglePlayer:
190 | var cs = SceneManager.SceneList.Find(s => s.GetType() == typeof(ChooseSongScene));
191 | if (cs == null)
192 | {
193 | cs = new ChooseSongScene(_GUIComponents, _centerPolygon, _player, _shaderProgram);
194 | SceneManager.AddScene(cs, this);
195 | }
196 | cs.Visible = true;
197 | break;
198 | case MainMenuOptions.Options:
199 | SceneManager.AddScene(new OptionsScene(), this);
200 | break;
201 | case MainMenuOptions.Exit:
202 | Exit();
203 | break;
204 | case MainMenuOptions.Update:
205 | SceneManager.AddScene(new UpdateScene(), this);
206 | break;
207 | }
208 | }
209 | }
210 |
211 | public override void Draw(double time)
212 | {
213 | _shaderProgram.Bind();
214 | _shaderProgram.SetUniform("mvp", SceneManager.ScreenCamera.WorldModelViewProjection);
215 | _shaderProgram.SetUniform("in_color", Color4.White);
216 |
217 | //Draw the player
218 | _player.Draw(time);
219 |
220 | //Draw the center polygon
221 | _centerPolygon.Draw(time);
222 |
223 | _shaderProgram.SetUniform("in_color", Color4.Black);
224 | _centerPolygon.DrawOutline(time);
225 |
226 | //Cleanup the program
227 | _shaderProgram.UnBind();
228 |
229 | _menuFontDrawing.RefreshBuffers();
230 | _menuFontDrawing.Draw();
231 |
232 | SceneManager.DrawTextLine("TURNT NINJA " + _gameVersion, new Vector3(-WindowWidth / 2+5, -WindowHeight / 2 + _versionFont.Font.MaxLineHeight, 0), Color.White, QFontAlignment.Left, _versionFont.Font);
233 | }
234 |
235 | public override void Dispose()
236 | {
237 | // Remove key remapping
238 | InputSystem.KeyRemappings.Remove(Key.KeypadEnter);
239 |
240 | _GUIComponents.Dispose();
241 | if (_shaderProgram != null)
242 | {
243 | _shaderProgram.Dispose();
244 | _shaderProgram = null;
245 | }
246 | _menuFontDrawing.Dispose();
247 | _menuFont.Dispose();
248 | }
249 |
250 | public override void EnterFocus()
251 | {
252 | }
253 |
254 | public override void ExitFocus()
255 | {
256 | }
257 | }
258 |
259 | enum MainMenuOptions
260 | {
261 | SinglePlayer = 1,
262 | Scores = 2,
263 | Options = 3,
264 | Exit = 4,
265 | Update = 5,
266 | ComingSoon = 0,
267 | None = -1
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/src/TurntNinja/Generation/StageGeometryBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using TurntNinja.Audio;
7 | using TurntNinja.Core;
8 | using TurntNinja.Game;
9 | using ColorMine.ColorSpaces;
10 | using OpenTK.Graphics;
11 | using Substructio.Core.Math;
12 | using Substructio.Graphics.OpenGL;
13 |
14 | namespace TurntNinja.Generation
15 | {
16 | class StageGeometryBuilder
17 | {
18 | private StageGeometry _stageGeometry;
19 | private AudioFeatures _audioFeatures;
20 | private GeometryBuilderOptions _builderOptions;
21 |
22 | private OnsetCollection _onsets;
23 | private OnsetDrawing _onsetDrawing;
24 | private float[] _beatFrequencies;
25 | private Color4 _segmentStartColour;
26 | private Random _random;
27 |
28 | private List _goodBeats;
29 |
30 | public StageGeometry Build(AudioFeatures audioFeatures, Random random, GeometryBuilderOptions builderOptions)
31 | {
32 | _audioFeatures = audioFeatures;
33 | _builderOptions = builderOptions;
34 | _random = random;
35 | _builderOptions.RandomFunction = _random;
36 |
37 | BuildGoodBeatsList();
38 | BuildBeatFrequencyList();
39 |
40 | BuildGeometry();
41 | SetStartColour();
42 |
43 | var backgroundPolygon = new PolarPolygon(6, new PolarVector(0.5, 0), 500000, -20, 0);
44 | backgroundPolygon.ShaderProgram = _builderOptions.GeometryShaderProgram;
45 |
46 | return new StageGeometry(_onsets, _onsetDrawing, _segmentStartColour, _random) {BackgroundPolygon = backgroundPolygon};
47 | }
48 |
49 | private void BuildGoodBeatsList()
50 | {
51 | //sort onset list by time
52 | var sorted = _audioFeatures.OnsetTimes.OrderBy(f => f);
53 | _goodBeats = new List();
54 | float prevTime = -1.0f;
55 |
56 | //filter out beats that are too close
57 | foreach (var b in sorted)
58 | {
59 | if (b - prevTime < _builderOptions.BeatSkipDistance)
60 | continue;
61 | _goodBeats.Add(b);
62 | prevTime = b;
63 | }
64 | }
65 |
66 | private void BuildBeatFrequencyList()
67 | {
68 | _beatFrequencies = new float[_goodBeats.Count];
69 | int lookAhead = 5;
70 | int halfFrequencySampleSize = 4;
71 | int forwardWeighting = 1;
72 |
73 | for (int i = 0; i < _beatFrequencies.Length; i++)
74 | {
75 | int weight = 0;
76 | float differenceSum = 0;
77 | int total = 0;
78 | //for (int j = i - halfFrequencySampleSize < 1 ? 1 : i-halfFrequencySampleSize; j <= i; j++)
79 | //{
80 | // weight++;
81 | // differenceSum += (sorted[j] - sorted[j-1]);
82 | // total += 1;
83 | //}
84 |
85 | //weight = halfFrequencySampleSize + forwardWeighting;
86 | int count = i + halfFrequencySampleSize + 1> _beatFrequencies.Length - 1 ? _beatFrequencies.Length - 1 : i + halfFrequencySampleSize + 1;
87 | for (int j = i+1; j <= count; j++)
88 | {
89 | differenceSum += (_goodBeats[j] - _goodBeats[j-1]);
90 | total += 1;
91 | //weight--;
92 | }
93 |
94 | _beatFrequencies[i] = 1 / (differenceSum / total);
95 | }
96 |
97 | _beatFrequencies[_beatFrequencies.Length - 1] = _beatFrequencies[_beatFrequencies.Length - 2];
98 |
99 | //_beatFrequencies = _audioFeatures.Onsets.Select(o => o.OnsetAmplitude).ToArray();
100 | }
101 |
102 | private void BuildGeometry()
103 | {
104 | _onsets = new OnsetCollection(_goodBeats.Count);
105 | _onsets.AddOnsets(_goodBeats.ToArray(), _beatFrequencies);
106 | _onsetDrawing = new OnsetDrawing(_onsets, _builderOptions.GeometryShaderProgram);
107 |
108 | //intialise state variables for algorithim
109 | int prevStart = 0;
110 | int prevSkip = 0;
111 | //set initial previous time to -1 so that the first polygon generated is always unique and doesn't trigger 'beat too close to previous' case
112 | double prevTime = -1.0;
113 | float samePatternChance = 0.45f;
114 | float veryCloseJoinChance = 0.45f;
115 | float joinFunctionMultiplier = 10.0f / 1.5f;
116 |
117 | bool[] sides;
118 |
119 | var structureList = new List>();
120 |
121 | List structures = new List();
122 |
123 | int currentStructureIndex = -1;
124 | //first pass to look for structures
125 | foreach (var b in _goodBeats)
126 | {
127 | // are we extending an existing structure?
128 | double t = Math.Exp(-joinFunctionMultiplier*(b - prevTime - _builderOptions.VeryCloseDistance) + Math.Log(veryCloseJoinChance));
129 | if (_random.NextDouble() < t)
130 | {
131 | structures[currentStructureIndex].End = b;
132 | }
133 |
134 | // else create new structure
135 | else
136 | {
137 | structures.Add(new OnsetStructure { Start = b, End = b });
138 | currentStructureIndex = structures.Count - 1;
139 | }
140 |
141 | // update previous onset time
142 | prevTime = b;
143 | }
144 |
145 | prevTime = -1;
146 |
147 | //traverse sorted onset list and generate geometry for each onset
148 | foreach (var s in structures)
149 | {
150 | int start;
151 |
152 | //generate the skip pattern. Highest probablility is of obtaining a 1 skip pattern - no sides are skipped at all.
153 | int skip = _builderOptions.SkipFunction();
154 | if (s.Start - prevTime < _builderOptions.VeryCloseDistance)
155 | {
156 | //this beat is very close to the previous one, use the same start orientation and skip pattern
157 | start = prevStart;
158 | skip = prevSkip;
159 | }
160 | else if (s.Start - prevTime < _builderOptions.CloseDistance)
161 | {
162 | if (_random.NextDouble() < 0.5)
163 | {
164 | //randomly choose relative orientation difference compared to previous beat
165 | var r = _random.Next(0, 2);
166 | if (r == 0) r = -1;
167 |
168 | //this beat is reasonably close to the previous one, use the same skip pattern but a different (+/- 1) orientation
169 | start = (prevStart + 6) + r;
170 | if (_random.NextDouble() < samePatternChance)
171 | skip = prevSkip;
172 | }
173 | else
174 | {
175 | start = prevStart;
176 | }
177 | }
178 | else
179 | {
180 | //choose a random start position for this polygon
181 | start = _random.Next(_builderOptions.MaxSides - 1);
182 | while (start == prevStart && _random.NextDouble() > 0.15)
183 | start = _random.Next(_builderOptions.MaxSides - 1);
184 | }
185 |
186 | sides = new bool[6];
187 | for (int i = 0; i < 6; i++)
188 | {
189 | //ensure that if skip is set to 1, we still leave an opening
190 | if (skip == 1 && i == start % 6) sides[i] = false;
191 | //if skip is not set to 1 and this is not a side we are skipping, enable this side
192 | else if ((i + start) % skip == 0) sides[i] = true;
193 | //else disable sides by default
194 | else sides[i] = false;
195 | }
196 |
197 | _onsetDrawing.AddOnsetDrawing(sides.ToList(), _builderOptions.PolygonVelocity, _builderOptions.PolygonWidth + (s.End - s.Start) * _builderOptions.PolygonVelocity.Radius, _builderOptions.PolygonMinimumRadius, s.Start);
198 |
199 | //update the variables holding the previous state of the algorithim.
200 | prevTime = s.End;
201 | prevStart = start;
202 | prevSkip = skip;
203 | }
204 | _onsetDrawing.Initialise();
205 | _onsets.Initialise();
206 | }
207 |
208 | private void SetStartColour()
209 | {
210 | //initialise algorithim values
211 | double maxStep = (double)360 / (20);
212 | double minStep = _builderOptions.MinimumColourStepMultiplier * maxStep;
213 | double startAngle = _random.NextDouble() * 360;
214 | double prevAngle = startAngle - maxStep;
215 |
216 | var step = _random.NextDouble() * (maxStep - minStep) + minStep;
217 | double angle = prevAngle;
218 | angle = MathUtilities.Normalise(step + angle, 0, 360);
219 | var rgb = HUSL.ColorConverter.HUSLToRGB(new List{angle, _builderOptions.Saturation, _builderOptions.Lightness});
220 |
221 | prevAngle = angle;
222 |
223 | _segmentStartColour = new Color4((byte)((rgb[0])*255), (byte)((rgb[1])*255), (byte)((rgb[2])*255), 255);
224 | }
225 | }
226 |
227 | class OnsetStructure
228 | {
229 | public double Start;
230 | public double End;
231 | }
232 |
233 | class GeometryBuilderOptions
234 | {
235 | public int MaxSides = 6;
236 |
237 | public PolarVector PolygonVelocity = new PolarVector(0, 600);
238 | public float PolygonWidth = 40f;
239 | public float PolygonMinimumRadius = 130f;
240 |
241 | public float VeryCloseDistance = 0.2f;
242 | public float CloseDistance = 0.4f;
243 | public float BeatSkipDistance = 0.0f;
244 |
245 | public Random RandomFunction;
246 |
247 | ///
248 | /// Method to use to get skip value.
249 | /// Default returns value between 1 and 3 inclusive
250 | ///
251 | /// Skip value
252 | public delegate int SkipDistributionFunction();
253 | public SkipDistributionFunction SkipFunction;
254 |
255 | public int Saturation = 50;
256 | public int Lightness = 30;
257 | public double MinimumColourStepMultiplier = 0.25;
258 |
259 | public ShaderProgram GeometryShaderProgram;
260 |
261 | public GeometryBuilderOptions(ShaderProgram geometryShaderProgram)
262 | {
263 | GeometryShaderProgram = geometryShaderProgram;
264 | SkipFunction = () => (int) (3*Math.Pow(RandomFunction.NextDouble(), 2)) + 1;
265 | }
266 |
267 | public void ApplyDifficulty(DifficultyOptions options)
268 | {
269 | PolygonVelocity.Radius = options.Speed;
270 | VeryCloseDistance = options.VeryCloseDistance;
271 | CloseDistance = options.CloseDistance;
272 | BeatSkipDistance = options.BeatSkipDistance;
273 | }
274 | }
275 | }
276 |
--------------------------------------------------------------------------------