2019-01-24 17:43:03 +09:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-04-13 18:19:50 +09:00
2022-06-17 16:37:17 +09:00
#nullable disable
2020-10-23 15:33:38 +09:00
using System ;
2017-09-13 11:22:24 +02:00
using System.Collections.Generic ;
using System.Linq ;
2020-10-23 15:33:38 +09:00
using osu.Framework.Graphics.Textures ;
2020-03-12 23:29:11 -07:00
using osu.Game.Beatmaps ;
2021-11-19 16:07:55 +09:00
using osu.Game.Extensions ;
2022-03-02 20:33:46 +03:00
using osu.Game.Rulesets.Mods ;
2020-03-12 23:29:11 -07:00
using osu.Game.Storyboards.Drawables ;
2018-04-13 18:19:50 +09:00
2017-09-13 11:22:24 +02:00
namespace osu.Game.Storyboards
{
2019-05-10 16:31:22 +09:00
public class Storyboard
2017-09-13 11:22:24 +02:00
{
private readonly Dictionary < string , StoryboardLayer > layers = new Dictionary < string , StoryboardLayer > ( ) ;
public IEnumerable < StoryboardLayer > Layers = > layers . Values ;
2018-04-13 18:19:50 +09:00
2018-02-16 12:07:59 +09:00
public BeatmapInfo BeatmapInfo = new BeatmapInfo ( ) ;
2018-04-13 18:19:50 +09:00
2020-10-19 23:32:04 +02:00
/// <summary>
/// Whether the storyboard can fall back to skin sprites in case no matching storyboard sprites are found.
/// </summary>
public bool UseSkinSprites { get ; set ; }
2017-09-15 11:23:37 +02:00
public bool HasDrawable = > Layers . Any ( l = > l . Elements . Any ( e = > e . IsDrawable ) ) ;
2018-04-13 18:19:50 +09:00
2021-01-04 15:16:01 +09:00
/// <summary>
/// Across all layers, find the earliest point in time that a storyboard element exists at.
/// Will return null if there are no elements.
/// </summary>
/// <remarks>
/// This iterates all elements and as such should be used sparingly or stored locally.
/// </remarks>
public double? EarliestEventTime = > Layers . SelectMany ( l = > l . Elements ) . OrderBy ( e = > e . StartTime ) . FirstOrDefault ( ) ? . StartTime ;
2019-03-26 16:18:15 +09:00
2021-04-12 16:02:19 -04:00
/// <summary>
/// Across all layers, find the latest point in time that a storyboard element ends at.
/// Will return null if there are no elements.
/// </summary>
2021-04-17 12:34:38 -04:00
/// <remarks>
/// This iterates all elements and as such should be used sparingly or stored locally.
/// Videos and samples return StartTime as their EndTIme.
/// </remarks>
2021-04-17 15:27:22 -04:00
public double? LatestEventTime = > Layers . SelectMany ( l = > l . Elements ) . OrderBy ( e = > e . GetEndTime ( ) ) . LastOrDefault ( ) ? . GetEndTime ( ) ;
2021-04-14 00:04:03 -04:00
2020-05-18 21:01:13 +02:00
/// <summary>
/// Depth of the currently front-most storyboard layer, excluding the overlay layer.
/// </summary>
private int minimumLayerDepth ;
2017-09-13 11:22:24 +02:00
public Storyboard ( )
{
2021-05-25 18:50:33 +09:00
layers . Add ( "Video" , new StoryboardVideoLayer ( "Video" , 4 , false ) ) ;
2017-09-13 11:22:24 +02:00
layers . Add ( "Background" , new StoryboardLayer ( "Background" , 3 ) ) ;
2020-03-25 11:08:08 +09:00
layers . Add ( "Fail" , new StoryboardLayer ( "Fail" , 2 ) { VisibleWhenPassing = false , } ) ;
layers . Add ( "Pass" , new StoryboardLayer ( "Pass" , 1 ) { VisibleWhenFailing = false , } ) ;
2020-05-18 21:01:13 +02:00
layers . Add ( "Foreground" , new StoryboardLayer ( "Foreground" , minimumLayerDepth = 0 ) ) ;
layers . Add ( "Overlay" , new StoryboardLayer ( "Overlay" , int . MinValue ) ) ;
2017-09-13 11:22:24 +02:00
}
2018-04-13 18:19:50 +09:00
2017-09-13 11:22:24 +02:00
public StoryboardLayer GetLayer ( string name )
{
2019-11-12 18:22:35 +08:00
if ( ! layers . TryGetValue ( name , out var layer ) )
2020-05-18 21:01:13 +02:00
layers [ name ] = layer = new StoryboardLayer ( name , - - minimumLayerDepth ) ;
2018-04-13 18:19:50 +09:00
2017-09-13 11:22:24 +02:00
return layer ;
}
2018-04-13 18:19:50 +09:00
2017-09-25 10:40:22 +02:00
/// <summary>
/// Whether the beatmap's background should be hidden while this storyboard is being displayed.
/// </summary>
2018-02-16 12:07:59 +09:00
public bool ReplacesBackground
2017-09-25 10:40:22 +02:00
{
2018-02-16 12:07:59 +09:00
get
{
2022-02-18 16:24:18 +09:00
string backgroundPath = BeatmapInfo . Metadata . BackgroundFile ;
2021-11-04 17:24:40 +09:00
2021-11-04 14:50:39 +09:00
if ( string . IsNullOrEmpty ( backgroundPath ) )
2018-02-16 12:07:59 +09:00
return false ;
2018-04-13 18:19:50 +09:00
2021-11-04 17:24:40 +09:00
// Importantly, do this after the NullOrEmpty because EF may have stored the non-nullable value as null to the database, bypassing compile-time constraints.
backgroundPath = backgroundPath . ToLowerInvariant ( ) ;
2020-03-12 23:29:11 -07:00
return GetLayer ( "Background" ) . Elements . Any ( e = > e . Path . ToLowerInvariant ( ) = = backgroundPath ) ;
2018-02-16 12:07:59 +09:00
}
2017-09-25 10:40:22 +02:00
}
2018-04-13 18:19:50 +09:00
2022-03-04 12:05:02 +09:00
public DrawableStoryboard CreateDrawable ( IReadOnlyList < Mod > mods = null ) = >
2022-03-02 20:33:46 +03:00
new DrawableStoryboard ( this , mods ) ;
2020-10-23 15:33:38 +09:00
2022-04-07 17:26:31 +09:00
public Texture GetTextureFromPath ( string path , TextureStore textureStore )
2020-10-23 15:33:38 +09:00
{
2022-01-12 18:05:25 +09:00
string storyboardPath = BeatmapInfo . BeatmapSet ? . Files . FirstOrDefault ( f = > f . Filename . Equals ( path , StringComparison . OrdinalIgnoreCase ) ) ? . File . GetStoragePath ( ) ;
2020-10-23 15:33:38 +09:00
2021-11-04 17:24:40 +09:00
if ( ! string . IsNullOrEmpty ( storyboardPath ) )
2022-04-07 17:26:31 +09:00
return textureStore . Get ( storyboardPath ) ;
2020-10-23 15:33:38 +09:00
2022-04-07 17:26:31 +09:00
return null ;
2020-10-23 15:33:38 +09:00
}
2017-09-13 11:22:24 +02:00
}
}