mirror of
https://github.com/RHeavenStudioPlus/HeavenStudioPlus.git
synced 2024-11-29 04:53:06 +00:00
37b80a33c7
commita6ce193ce5
Merge:6105bb4f
9b24ac48
Author: minenice55 <star.elementa@gmail.com> Date: Wed Jun 14 01:23:31 2023 +0000 Merge pull request #473 from minenice55/easings-fix Enum Parsing Fix commit9b24ac4838
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 21:21:13 2023 -0400 fix all parsing of enums, removed the "special case" for eases update to latest Jukebox commit6105bb4f64
Merge:576b4a00
8df235a8
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 21:39:15 2023 +0000 Merge pull request #469 from minenice55/jukebox-update Jukebox Package Update commit576b4a0017
Merge:99804457
10f8fa7f
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 21:39:08 2023 +0000 Merge pull request #458 from AstrlJelly/MiscAdditions_4 Bug Fixes + Feature Additions commit10f8fa7f5a
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 17:38:24 2023 -0400 add "updater" for the old marching entity commit118cd4993d
Merge:1c1c731d
99804457
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 17:22:01 2023 -0400 Merge branch 'release_1' into pr/458 commit8df235a85b
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 16:55:02 2023 -0400 let play mode start if no song file is loaded fix issue with loading large audio files commit9980445723
Merge:aff1e3d8
2f595824
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 18:32:40 2023 +0000 Merge pull request #471 from Rapandrasmus/FixWorkingDoughCurve Fixed weird curve stuff on game switch in working dough commit2f59582460
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 19:24:13 2023 +0200 Fixed weird curve stuff on game switch in working dough commitaff1e3d889
Merge:b40ec128
c9accfa5
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 16:09:21 2023 +0000 Merge pull request #470 from Rapandrasmus/WorkingDoughFixes Fixed small balls not working in working dough commitc9accfa52d
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 18:07:13 2023 +0200 Fixed small balls not working in working dough commitb40ec128a5
Merge:9f953d50
95c9b346
Author: minenice55 <star.elementa@gmail.com> Date: Tue Jun 13 14:56:58 2023 +0000 Merge pull request #467 from Rapandrasmus/WorkingDoughRework Working Dough rework commit95c9b34695
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 16:13:08 2023 +0200 modernised rockers pass turn commit0debf55391
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 16:03:08 2023 +0200 Rhythm tweezers pass turn now works like working dough commit6eac745eae
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 15:51:35 2023 +0200 gandw on balls has been added commit349867d983
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 14:59:56 2023 +0200 Proper inactive handling now commitd469c6c3fa
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 13:52:15 2023 +0200 OnSpawnBall reimplemented commitdd290b3331
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Tue Jun 13 13:37:09 2023 +0200 new sounds commit757e1c2c5e
Author: minenice55 <star.elementa@gmail.com> Date: Mon Jun 12 17:18:37 2023 -0400 update Jukebox to latest version fixes for inferred entity loading commit8ce1d8276b
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 21:36:57 2023 +0200 ball transporter anims for pass turn commitaf0d9bae19
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 20:38:48 2023 +0200 working dough converted, need to fix eveerything though commitdb21d51673
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 18:36:41 2023 +0200 Converted everything to new curves and made playerballs handle themselves input-wise commit41157a038c
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 14:04:32 2023 +0200 Some new curves commit7d754f5706
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 12:03:21 2023 +0200 Rockers can do it too now commitc196947251
Author: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com> Date: Mon Jun 12 11:58:31 2023 +0200 You can now place inputs on top of pass turn for rhythm tweezers commit9f953d505f
Merge:2a8d8d6f
ce3e6f2d
Author: minenice55 <star.elementa@gmail.com> Date: Sun Jun 11 15:55:40 2023 -0400 Merge branch 'master' into release_1 commit2a8d8d6fd2
Merge:9e3e5942
13adf5b0
Author: minenice55 <star.elementa@gmail.com> Date: Sun Jun 11 19:53:47 2023 +0000 Merge pull request #465 from minenice55/dont_infer_track Don't infer the track field when importing converted v0 riq coming from unknown origin commit13adf5b07b
Author: minenice55 <star.elementa@gmail.com> Date: Sun Jun 11 15:52:14 2023 -0400 don't infer track when importing a v0 riq from another program commit9e3e594209
Author: minenice55 <star.elementa@gmail.com> Date: Sun Jun 11 12:12:25 2023 -0400 make base datamodels for special entity reading (#463) * make base datamodels for special entity reading * fix crop stomp breaking when no game switch or remix end is set * fix save shortcut fix loading charts with no music commit1c1c731dd1
Author: minenice55 <star.elementa@gmail.com> Date: Sat Jun 10 23:00:30 2023 -0400 add updater for marching orders turn commit210be54ba3
Merge:67b6658c
af8395fb
Author: minenice55 <star.elementa@gmail.com> Date: Sat Jun 10 22:36:10 2023 -0400 Merge branch 'release_1' into pr/458 commitaf8395fb6a
Merge:81eafec9
afc665ed
Author: minenice55 <star.elementa@gmail.com> Date: Sat Jun 10 22:30:32 2023 -0400 Merge branch 'master' into release_1 commit81eafec986
Author: minenice55 <star.elementa@gmail.com> Date: Sat Jun 10 22:27:58 2023 -0400 editor fixes (#459) * ditch loading dialog doesn't show up when it's supposed to * format song offset in editor * remove VorbisPlugin * Update Editor.cs commit67b6658cd3
Author: AstrlJelly <bdlawson115@gmail.com> Date: Sat Jun 10 16:55:10 2023 -0400 convert float to double and all that commita4b66367fd
Merge:08a664ba
afc665ed
Author: AstrlJelly <bdlawson115@gmail.com> Date: Sat Jun 10 16:01:44 2023 -0400 Merge remote-tracking branch 'upstream/master' into MiscAdditions_4 commit08a664ba2a
Merge:faf3fbf9
2b0ced2f
Author: AstrlJelly <bdlawson115@gmail.com> Date: Sat Jun 10 15:21:22 2023 -0400 Merge remote-tracking branch 'upstream/master' into MiscAdditions_4 commitfaf3fbf97c
Author: AstrlJelly <bdlawson115@gmail.com> Date: Sat Jun 10 15:18:45 2023 -0400 a lot * munchy monk input + mustache fixes * fork lifter and pajama party bopping * meat grinder miss bop fix * cloud monkey Real * marching orders Go! was broken * force march doesn't break when it's too early from a game switch * you can use the March! block without the marching now commitbb2ae74339
Author: minenice55 <star.elementa@gmail.com> Date: Sat Jun 10 15:13:29 2023 -0400 Integration of Jukebox Library (#451) * add Jukebox library todo: - saving / loading of new format - inferrence of unknown data like past versions - move the temporary float casts to proper use of double - make sound related functions take double for timing - inform people that the Jukebox sound player was renamed to SoundByte lol * make sound, input scheduling, and super curve use double precision * successfully load charts * editor works again v1 riqs can be saved and loaded * first tempo and volume markers are unmovable fix loading of charts' easing values * use gsync / freesync * update Jukebox refs to SoundByte * game events use double part 1 Air Rally - Glee Club converted * don't load song if chart load fails * finish conversion of all minigames * remove editor waveform toggle * timeline now respects added song offset length clear cache files on app close prepped notes for dsp sync * update timeline length when offset changed * update to latest Jukebox * make error panel object in global game manager * improve conductor music scheduling * added error message box fix first game events sometimes not playing
754 lines
No EOL
41 KiB
C#
754 lines
No EOL
41 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
using DG.Tweening;
|
|
|
|
using HeavenStudio.Util;
|
|
using HeavenStudio.Editor.Track;
|
|
using HeavenStudio.Games;
|
|
using Jukebox;
|
|
using Jukebox.Legacy;
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.IO;
|
|
|
|
namespace HeavenStudio
|
|
{
|
|
|
|
public class Minigames
|
|
{
|
|
|
|
public static void InitPreprocessor()
|
|
{
|
|
RiqBeatmap.OnUpdateBeatmap += PreProcessBeatmap;
|
|
}
|
|
|
|
public static Dictionary<string, object> propertiesModel = new()
|
|
{
|
|
// mapper set properties? (future: use this to flash the button)
|
|
{"propertiesmodified", false},
|
|
|
|
////// CATEGORY 1: SONG INFO
|
|
// general chart info
|
|
{"remixtitle", "New Remix"}, // chart name
|
|
{"remixauthor", "Your Name"}, // charter's name
|
|
{"remixdesc", "Remix Description"}, // chart description
|
|
{"remixlevel", 1}, // chart difficulty (maybe offer a suggestion but still have the mapper determine it)
|
|
{"remixtempo", 120f}, // avg. chart tempo
|
|
{"remixtags", ""}, // chart tags
|
|
{"icontype", 0}, // chart icon (presets, custom - future)
|
|
{"iconurl", ""}, // custom icon location (future)
|
|
{"challengetype", 0}, // perfect challenge type
|
|
|
|
// chart song info
|
|
{"idolgenre", "Song Genre"}, // song genre
|
|
{"idolsong", "Song Name"}, // song name
|
|
{"idolcredit", "Artist"}, // song artist
|
|
|
|
////// CATEGORY 2: PROLOGUE AND EPILOGUE
|
|
// chart prologue
|
|
{"prologuetype", 0}, // prologue card animation (future)
|
|
{"prologuecaption", "Remix"}, // prologue card sub-title (future)
|
|
|
|
// chart results screen messages
|
|
{"resultcaption", "Rhythm League Notes"}, // result screen header
|
|
{"resultcommon_hi", "Good rhythm."}, // generic "Superb" message (one-liner, or second line for single-type)
|
|
{"resultcommon_ok", "Eh. Passable."}, // generic "OK" message (one-liner, or second line for single-type)
|
|
{"resultcommon_ng", "Try harder next time."}, // generic "Try Again" message (one-liner, or second line for single-type)
|
|
|
|
// the following are shown / hidden in-editor depending on the tags of the games used
|
|
{"resultnormal_hi", "You show strong fundamentals."}, // "Superb" message for normal games (two-liner)
|
|
{"resultnormal_ng", "Work on your fundamentals."}, // "Try Again" message for normal games (two-liner)
|
|
|
|
{"resultkeep_hi", "You kept the beat well."}, // "Superb" message for keep-the-beat games (two-liner)
|
|
{"resultkeep_ng", "You had trouble keeping the beat."}, // "Try Again" message for keep-the-beat games (two-liner)
|
|
|
|
{"resultaim_hi", "You had great aim."}, // "Superb" message for aim games (two-liner)
|
|
{"resultaim_ng", "Your aim was a little shaky."}, // "Try Again" message for aim games (two-liner)
|
|
|
|
{"resultrepeat_hi", "You followed the example well."}, // "Superb" message for call-and-response games (two-liner)
|
|
{"resultrepeat_ng", "Next time, follow the example better."}, // "Try Again" message for call-and-response games (two-liner)
|
|
};
|
|
|
|
static Dictionary<string, object> tempoChangeModel = new()
|
|
{
|
|
{"tempo", 120f},
|
|
{"swing", 0f},
|
|
{"timeSignature", new Vector2(4, 4)},
|
|
};
|
|
|
|
static Dictionary<string, object> volumeChangeModel = new()
|
|
{
|
|
{"volume", 1f},
|
|
{"fade", Util.EasingFunction.Ease.Instant},
|
|
};
|
|
|
|
static Dictionary<string, object> sectionMarkModel = new()
|
|
{
|
|
{"sectionName", ""},
|
|
{"isCheckpoint", false},
|
|
{"startPerfect", false},
|
|
{"breakSection", false},
|
|
{"extendsPrevious", false},
|
|
{"sectionWeight", 1f},
|
|
};
|
|
|
|
static void PreProcessSpecialEntity(RiqEntity e, Dictionary<string, object> model)
|
|
{
|
|
foreach (var t in model)
|
|
{
|
|
string propertyName = t.Key;
|
|
Type type = t.Value.GetType();
|
|
if (!e.dynamicData.ContainsKey(propertyName))
|
|
{
|
|
e.CreateProperty(propertyName, t.Value);
|
|
}
|
|
Type pType = e[propertyName].GetType();
|
|
if (pType != type)
|
|
{
|
|
try
|
|
{
|
|
if (type.IsEnum)
|
|
{
|
|
if (pType == typeof(string))
|
|
e.dynamicData[propertyName] = (int)Enum.Parse(type, (string)e[propertyName]);
|
|
else
|
|
e.dynamicData[propertyName] = (int)e[propertyName];
|
|
}
|
|
else if (pType == typeof(Newtonsoft.Json.Linq.JObject))
|
|
e[propertyName] = e[propertyName].ToObject(type);
|
|
else
|
|
e[propertyName] = Convert.ChangeType(e[propertyName], type);
|
|
}
|
|
catch
|
|
{
|
|
Debug.LogWarning($"Could not convert {propertyName} to {type}! Using default value...");
|
|
// use default value
|
|
e.CreateProperty(propertyName, t.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// processes an riq beatmap after it is loaded
|
|
/// </summary>
|
|
public static RiqBeatmapData? PreProcessBeatmap(string version, RiqBeatmapData data)
|
|
{
|
|
Debug.Log("Preprocessing beatmap...");
|
|
Minigames.Minigame game;
|
|
Minigames.GameAction action;
|
|
System.Type type, pType;
|
|
foreach (var e in data.entities)
|
|
{
|
|
var gameName = e.datamodel.Split(0);
|
|
var actionName = e.datamodel.Split(1);
|
|
game = EventCaller.instance.GetMinigame(gameName);
|
|
if (game == null)
|
|
{
|
|
Debug.LogWarning($"Unknown game {gameName} found in remix.json! Adding game...");
|
|
game = new Minigames.Minigame(gameName, gameName.DisplayName() + " \n<color=#eb5454>[inferred from remix.json]</color>", "", false, false, new List<Minigames.GameAction>(), inferred: true);
|
|
EventCaller.instance.minigames.Add(game);
|
|
if (Editor.Editor.instance != null)
|
|
Editor.Editor.instance.AddIcon(game);
|
|
}
|
|
action = EventCaller.instance.GetGameAction(game, actionName);
|
|
if (action == null)
|
|
{
|
|
Debug.LogWarning($"Unknown action {gameName}/{actionName} found in remix.json! Adding action...");
|
|
var parameters = new List<Minigames.Param>();
|
|
foreach (var item in e.dynamicData)
|
|
{
|
|
Debug.Log($"k: {item.Key}, v: {item.Value}");
|
|
if (item.Key == "track")
|
|
continue;
|
|
if (item.Value == null)
|
|
continue;
|
|
var value = item.Value;
|
|
if (value.GetType() == typeof(long))
|
|
value = new EntityTypes.Integer(int.MinValue, int.MaxValue, (int)value);
|
|
else if (value.GetType() == typeof(double))
|
|
value = new EntityTypes.Float(float.NegativeInfinity, float.PositiveInfinity, (float)value);
|
|
parameters.Add(new Minigames.Param(item.Key, value, item.Key.DisplayName(), "[inferred from remix.json]"));
|
|
}
|
|
action = new Minigames.GameAction(actionName, actionName.DisplayName(), e.length, true, parameters);
|
|
game.actions.Add(action);
|
|
}
|
|
|
|
//check each param of the action
|
|
if (action.parameters != null)
|
|
{
|
|
foreach (var param in action.parameters)
|
|
{
|
|
type = param.parameter.GetType();
|
|
//add property if it doesn't exist
|
|
if (!e.dynamicData.ContainsKey(param.propertyName))
|
|
{
|
|
Debug.LogWarning($"Property {param.propertyName} does not exist in the entity's dynamic data! Adding...");
|
|
if (type == typeof(EntityTypes.Integer))
|
|
e.dynamicData.Add(param.propertyName, ((EntityTypes.Integer)param.parameter).val);
|
|
else if (type == typeof(EntityTypes.Float))
|
|
e.dynamicData.Add(param.propertyName, ((EntityTypes.Float)param.parameter).val);
|
|
else if (type.IsEnum)
|
|
e.dynamicData.Add(param.propertyName, (int)param.parameter);
|
|
else
|
|
e.dynamicData.Add(param.propertyName, Convert.ChangeType(param.parameter, type));
|
|
continue;
|
|
}
|
|
pType = e[param.propertyName].GetType();
|
|
if (pType != type)
|
|
{
|
|
try
|
|
{
|
|
if (type == typeof(EntityTypes.Integer))
|
|
e.dynamicData[param.propertyName] = (int)e[param.propertyName];
|
|
else if (type == typeof(EntityTypes.Float))
|
|
e.dynamicData[param.propertyName] = (float)e[param.propertyName];
|
|
else if (type.IsEnum)
|
|
{
|
|
if (pType == typeof(string))
|
|
e.dynamicData[param.propertyName] = (int)Enum.Parse(type, (string)e[param.propertyName]);
|
|
else
|
|
e.dynamicData[param.propertyName] = (int)e[param.propertyName];
|
|
}
|
|
else if (pType == typeof(Newtonsoft.Json.Linq.JObject))
|
|
e.dynamicData[param.propertyName] = e[param.propertyName].ToObject(type);
|
|
else
|
|
e.dynamicData[param.propertyName] = Convert.ChangeType(e[param.propertyName], type);
|
|
}
|
|
catch
|
|
{
|
|
Debug.LogWarning($"Could not convert {param.propertyName} to {type}! Using default value...");
|
|
// GlobalGameManager.ShowErrorMessage("Warning", $"Could not convert {e.datamodel}/{param.propertyName} to {type}! This will be loaded using the default value, so chart may be unstable.");
|
|
// use default value
|
|
if (type == typeof(EntityTypes.Integer))
|
|
e.dynamicData[param.propertyName] = ((EntityTypes.Integer)param.parameter).val;
|
|
else if (type == typeof(EntityTypes.Float))
|
|
e.dynamicData[param.propertyName] = ((EntityTypes.Float)param.parameter).val;
|
|
else if (type.IsEnum && param.propertyName != "ease")
|
|
e.dynamicData[param.propertyName] = (int)param.parameter;
|
|
else
|
|
e.dynamicData[param.propertyName] = Convert.ChangeType(param.parameter, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var tempo in data.tempoChanges)
|
|
{
|
|
PreProcessSpecialEntity(tempo, tempoChangeModel);
|
|
}
|
|
if (data.tempoChanges[0]["tempo"] <= 0)
|
|
{
|
|
data.tempoChanges[0]["tempo"] = 120;
|
|
}
|
|
|
|
foreach (var vol in data.volumeChanges)
|
|
{
|
|
PreProcessSpecialEntity(vol, volumeChangeModel);
|
|
}
|
|
|
|
foreach (var section in data.beatmapSections)
|
|
{
|
|
PreProcessSpecialEntity(section, sectionMarkModel);
|
|
}
|
|
|
|
//go thru each property of the model beatmap and add any missing keyvalue pair
|
|
foreach (var prop in propertiesModel)
|
|
{
|
|
if (!data.properties.ContainsKey(prop.Key))
|
|
{
|
|
data.properties.Add(prop.Key, prop.Value);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
public class Minigame
|
|
{
|
|
|
|
public string name;
|
|
public string displayName;
|
|
public string color;
|
|
public GameObject holder;
|
|
public bool hidden;
|
|
public bool fxOnly;
|
|
public List<GameAction> actions = new List<GameAction>();
|
|
|
|
public List<string> tags;
|
|
public string defaultLocale = "en";
|
|
public string wantAssetBundle = "";
|
|
public List<string> supportedLocales;
|
|
public bool inferred;
|
|
|
|
public bool usesAssetBundle => (wantAssetBundle != "");
|
|
public bool hasLocales => (supportedLocales.Count > 0);
|
|
public bool AssetsLoaded => (((hasLocales && localeLoaded && currentLoadedLocale == defaultLocale) || (!hasLocales)) && commonLoaded);
|
|
public bool SequencesPreloaded => soundSequences != null;
|
|
public string LoadableName => inferred ? "noGame" : name;
|
|
|
|
private AssetBundle bundleCommon = null;
|
|
private bool commonLoaded = false;
|
|
private bool commonPreloaded = false;
|
|
private string currentLoadedLocale = "";
|
|
private AssetBundle bundleLocalized = null;
|
|
private bool localeLoaded = false;
|
|
private bool localePreloaded = false;
|
|
|
|
private SoundSequence.SequenceKeyValue[] soundSequences = null;
|
|
|
|
public SoundSequence.SequenceKeyValue[] LoadedSoundSequences
|
|
{
|
|
get => soundSequences;
|
|
set => soundSequences = value;
|
|
}
|
|
|
|
public Minigame(string name, string displayName, string color, bool hidden, bool fxOnly, List<GameAction> actions, List<string> tags = null, string assetBundle = "", string defaultLocale = "en", List<string> supportedLocales = null, bool inferred = false)
|
|
{
|
|
this.name = name;
|
|
this.displayName = displayName;
|
|
this.color = color;
|
|
this.actions = actions;
|
|
this.hidden = hidden;
|
|
this.fxOnly = fxOnly;
|
|
|
|
this.tags = tags ?? new List<string>();
|
|
this.wantAssetBundle = assetBundle;
|
|
this.defaultLocale = defaultLocale;
|
|
this.supportedLocales = supportedLocales ?? new List<string>();
|
|
this.inferred = inferred;
|
|
}
|
|
|
|
public AssetBundle GetLocalizedAssetBundle()
|
|
{
|
|
if (!hasLocales) return null;
|
|
if (!usesAssetBundle) return null;
|
|
if (bundleLocalized == null || currentLoadedLocale != defaultLocale) //TEMPORARY: use the game's default locale until we add localization support
|
|
{
|
|
if (localeLoaded) return bundleLocalized;
|
|
// TODO: try/catch for missing assetbundles
|
|
currentLoadedLocale = defaultLocale;
|
|
bundleLocalized = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, wantAssetBundle + "/locale." + defaultLocale));
|
|
localeLoaded = true;
|
|
}
|
|
return bundleLocalized;
|
|
}
|
|
|
|
public AssetBundle GetCommonAssetBundle()
|
|
{
|
|
if (commonLoaded) return bundleCommon;
|
|
if (!usesAssetBundle) return null;
|
|
if (bundleCommon == null)
|
|
{
|
|
// TODO: try/catch for missing assetbundles
|
|
bundleCommon = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, wantAssetBundle + "/common"));
|
|
commonLoaded = true;
|
|
}
|
|
return bundleCommon;
|
|
}
|
|
|
|
public IEnumerator LoadCommonAssetBundleAsync()
|
|
{
|
|
if (commonPreloaded || commonLoaded) yield break;
|
|
commonPreloaded = true;
|
|
if (!usesAssetBundle) yield break;
|
|
if (bundleCommon != null) yield break;
|
|
|
|
AssetBundleCreateRequest asyncBundleRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, wantAssetBundle + "/common"));
|
|
if (bundleCommon != null) yield break;
|
|
yield return asyncBundleRequest;
|
|
|
|
AssetBundle localAssetBundle = asyncBundleRequest.assetBundle;
|
|
if (bundleCommon != null) yield break;
|
|
yield return localAssetBundle;
|
|
|
|
if (localAssetBundle == null) yield break;
|
|
|
|
bundleCommon = localAssetBundle;
|
|
commonLoaded = true;
|
|
}
|
|
|
|
public IEnumerator LoadLocalizedAssetBundleAsync()
|
|
{
|
|
if (localePreloaded) yield break;
|
|
localePreloaded = true;
|
|
if (!hasLocales) yield break;
|
|
if (!usesAssetBundle) yield break;
|
|
if (localeLoaded && bundleLocalized != null && currentLoadedLocale == defaultLocale) yield break;
|
|
|
|
AssetBundleCreateRequest asyncBundleRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, wantAssetBundle + "/locale." + defaultLocale));
|
|
if (localeLoaded && bundleLocalized != null && currentLoadedLocale == defaultLocale) yield break;
|
|
yield return asyncBundleRequest;
|
|
|
|
AssetBundle localAssetBundle = asyncBundleRequest.assetBundle;
|
|
if (localeLoaded && bundleLocalized != null && currentLoadedLocale == defaultLocale) yield break;
|
|
yield return localAssetBundle;
|
|
|
|
if (localAssetBundle == null) yield break;
|
|
|
|
bundleLocalized = localAssetBundle;
|
|
currentLoadedLocale = defaultLocale;
|
|
localeLoaded = true;
|
|
}
|
|
}
|
|
|
|
public class GameAction
|
|
{
|
|
public string actionName;
|
|
public string displayName;
|
|
public EventCallback function = delegate { };
|
|
public float defaultLength = 1;
|
|
public bool resizable = false;
|
|
public List<Param> parameters = null;
|
|
public bool hidden = false;
|
|
public int priority = 0;
|
|
public EventCallback inactiveFunction = delegate { };
|
|
public EventCallback preFunction = delegate { };
|
|
public float preFunctionLength = 2.0f;
|
|
|
|
/// <summary>
|
|
/// <para>Creates a block that can be used in the editor. The block's function and attributes are defined in the parentheses.</para>
|
|
/// <para>Note: Every parameter after the second one is an optional parameter. You can change optional parameters by adding (name): (value) after the second parameter.</para>
|
|
/// </summary>
|
|
/// <param name="actionName">Entity model name</param>
|
|
/// <param name="displayName">Name of the block used in the UI</param>
|
|
/// <param name="defaultLength">How long the block appears in the editor</param>
|
|
/// <param name="resizable">Allows the user to resize the block</param>
|
|
/// <param name="parameters">Extra parameters for this block that change how it functions.</param>
|
|
/// <param name="function"><para>What the block does when read during playback</para>
|
|
/// <para>Only does this if the game that it is associated with is loaded.</para></param>
|
|
/// <param name="inactiveFunction">What the block does when read while the game it's associated with isn't loaded.</param>
|
|
/// <param name="prescheduleFunction">What the block does when the GameManager seeks to this cue for pre-scheduling.</param>
|
|
/// <param name="hidden">Prevents the block from being shown in the game list. Block will still function normally if it is in the timeline.</param>
|
|
/// <param name="preFunction">Runs two beats before this event is reached.</param>
|
|
/// <param name="priority">Priority of this event. Higher priority events will be run first.</param>
|
|
public GameAction(string actionName, string displayName, float defaultLength = 1, bool resizable = false, List<Param> parameters = null, EventCallback function = null, EventCallback inactiveFunction = null, EventCallback prescheduleFunction = null, bool hidden = false, EventCallback preFunction = null, int priority = 0, float preFunctionLength = 2.0f)
|
|
{
|
|
this.actionName = actionName;
|
|
if (displayName == String.Empty) this.displayName = actionName;
|
|
else this.displayName = displayName;
|
|
this.defaultLength = defaultLength;
|
|
this.resizable = resizable;
|
|
this.parameters = parameters;
|
|
this.hidden = hidden;
|
|
|
|
this.function = function ?? delegate { };
|
|
this.inactiveFunction = inactiveFunction ?? delegate { };
|
|
this.preFunction = prescheduleFunction ?? delegate { };
|
|
this.priority = priority;
|
|
this.preFunctionLength = preFunctionLength;
|
|
|
|
|
|
//todo: converting to new versions of GameActions
|
|
}
|
|
|
|
/// <summary>
|
|
/// <para>Shorthand constructor for a GameAction with only required data</para>
|
|
/// </summary>
|
|
/// <param name="actionName">Entity model name</param>
|
|
/// <param name="displayName">Name of the block used in the UI</param>
|
|
public GameAction(string actionName, string displayName)
|
|
{
|
|
this.actionName = actionName;
|
|
if (displayName == String.Empty) this.displayName = actionName;
|
|
else this.displayName = displayName;
|
|
}
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class Param
|
|
{
|
|
public string propertyName;
|
|
public object parameter;
|
|
public string propertyCaption;
|
|
public string tooltip;
|
|
|
|
/// <summary>
|
|
/// A parameter that changes the function of a GameAction.
|
|
/// </summary>
|
|
/// <param name="propertyName">The name of the variable that's being changed.</param>
|
|
/// <param name="parameter">The value of the parameter</param>
|
|
/// <param name="propertyCaption">The name shown in the editor. Can be anything you want.</param>
|
|
public Param(string propertyName, object parameter, string propertyCaption, string tooltip = "")
|
|
{
|
|
this.propertyName = propertyName;
|
|
this.parameter = parameter;
|
|
this.propertyCaption = propertyCaption;
|
|
this.tooltip = tooltip;
|
|
}
|
|
}
|
|
|
|
public delegate void EventCallback();
|
|
public delegate void ParamChangeCallback(string paramName, object paramValue, RiqEntity entity);
|
|
|
|
// overengineered af but it's a modified version of
|
|
// https://stackoverflow.com/a/19877141
|
|
static List<Func<EventCaller, Minigame>> loadRunners;
|
|
static void BuildLoadRunnerList() {
|
|
loadRunners = System.Reflection.Assembly.GetExecutingAssembly()
|
|
.GetTypes()
|
|
.Where(x => x.Namespace == "HeavenStudio.Games.Loaders" && x.GetMethod("AddGame", BindingFlags.Public | BindingFlags.Static) != null)
|
|
.Select(t => (Func<EventCaller, Minigame>) Delegate.CreateDelegate(
|
|
typeof(Func<EventCaller, Minigame>),
|
|
null,
|
|
t.GetMethod("AddGame", BindingFlags.Public | BindingFlags.Static),
|
|
false
|
|
))
|
|
.ToList();
|
|
|
|
}
|
|
|
|
public static void Init(EventCaller eventCaller)
|
|
{
|
|
eventCaller.minigames = new List<Minigame>()
|
|
{
|
|
new Minigame("gameManager", "Game Manager", "", false, true, new List<GameAction>()
|
|
{
|
|
new GameAction("switchGame", "Switch Game", 0.5f, false,
|
|
function: delegate { var e = eventCaller.currentEntity; GameManager.instance.SwitchGame(eventCaller.currentSwitchGame, eventCaller.currentEntity.beat, e["toggle"]); },
|
|
parameters: new List<Param>()
|
|
{
|
|
new Param("toggle", true, "Black Flash", "Enable or disable the black screen for this Game Switch")
|
|
},
|
|
inactiveFunction: delegate { var e = eventCaller.currentEntity; GameManager.instance.SwitchGame(eventCaller.currentSwitchGame, eventCaller.currentEntity.beat, e["toggle"]); }
|
|
),
|
|
new GameAction("end", "End Remix",
|
|
function: delegate {
|
|
Debug.Log("end");
|
|
if (Timeline.instance != null)
|
|
Timeline.instance?.Stop(0);
|
|
else
|
|
GameManager.instance.Stop(0);
|
|
}
|
|
),
|
|
new GameAction("skill star", "Skill Star", 1f, true)
|
|
{
|
|
//temp for testing
|
|
function = delegate {
|
|
var e = eventCaller.currentEntity;
|
|
HeavenStudio.Common.SkillStarManager.instance.DoStarIn(e.beat, e.length);
|
|
// BeatAction.New(HeavenStudio.Common.SkillStarManager.instance.gameObject, new List<BeatAction.Action>(){
|
|
// new BeatAction.Action(e.beat + e.length, delegate {
|
|
// HeavenStudio.Common.SkillStarManager.instance.DoStarJust();
|
|
// })
|
|
// });
|
|
}
|
|
},
|
|
new GameAction("toggle inputs", "Toggle Inputs", 0.5f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("toggle", true, "Enable Inputs")
|
|
},
|
|
delegate
|
|
{
|
|
GameManager.instance.ToggleInputs(eventCaller.currentEntity["toggle"]);
|
|
}
|
|
),
|
|
|
|
// These are still here for backwards-compatibility but are hidden in the editor
|
|
new GameAction("flash", "", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("colorA", Color.white, "Start Color"),
|
|
new Param("colorB", Color.white, "End Color"),
|
|
new Param("valA", new EntityTypes.Float(0, 1, 1), "Start Opacity"),
|
|
new Param("valB", new EntityTypes.Float(0, 1, 0), "End Opacity"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease")
|
|
},
|
|
hidden: true
|
|
),
|
|
new GameAction("move camera", "", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left"),
|
|
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down"),
|
|
new Param("valC", new EntityTypes.Float(-0, 250, 10), "In / Out"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type")
|
|
},
|
|
hidden: true ),
|
|
new GameAction("rotate camera", "", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Integer(-360, 360, 0), "Pitch"),
|
|
new Param("valB", new EntityTypes.Integer(-360, 360, 0), "Yaw"),
|
|
new Param("valC", new EntityTypes.Integer(-360, 360, 0), "Roll"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type")
|
|
},
|
|
hidden: true ),
|
|
}),
|
|
|
|
new Minigame("countIn", "Count-Ins", "", false, true, new List<GameAction>()
|
|
{
|
|
new GameAction("4 beat count-in", "4 Beat Count-In", 4f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("type", SoundEffects.CountInType.Normal, "Type", "The sounds to play for the count-in")
|
|
},
|
|
delegate { var e = eventCaller.currentEntity; SoundEffects.FourBeatCountIn(e.beat, e.length / 4f, e["type"]); }
|
|
),
|
|
new GameAction("8 beat count-in", "8 Beat Count-In", 8f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("type", SoundEffects.CountInType.Normal, "Type", "The sounds to play for the count-in")
|
|
},
|
|
delegate { var e = eventCaller.currentEntity; SoundEffects.EightBeatCountIn(e.beat, e.length / 8f, e["type"]); }
|
|
),
|
|
new GameAction("count", "Count", 1f, false,
|
|
new List<Param>()
|
|
{
|
|
new Param("type", SoundEffects.CountNumbers.One, "Number", "The sound to play"),
|
|
new Param("toggle", false, "Alt", "Whether or not the alternate version should be played")
|
|
},
|
|
delegate { var e = eventCaller.currentEntity; SoundEffects.Count(e["type"], e["toggle"]); }
|
|
),
|
|
new GameAction("cowbell", "Cowbell",
|
|
function: delegate { SoundEffects.Cowbell(); }
|
|
),
|
|
new GameAction("ready!", "Ready!", 2f, true,
|
|
function: delegate { var e = eventCaller.currentEntity; SoundEffects.Ready(e.beat, e.length / 2f); }
|
|
),
|
|
new GameAction("and", "And", 0.5f,
|
|
function: delegate { SoundEffects.And(); }
|
|
),
|
|
new GameAction("go!", "Go!", 1f, false,
|
|
new List<Param>()
|
|
{
|
|
new Param("toggle", false, "Alt", "Whether or not the alternate version should be played")
|
|
},
|
|
function: delegate { SoundEffects.Go(eventCaller.currentEntity["toggle"]); }
|
|
),
|
|
|
|
// These are still here for backwards-compatibility but are hidden in the editor
|
|
new GameAction("4 beat count-in (alt)", "", 4f, function: delegate { var e = eventCaller.currentEntity; SoundEffects.FourBeatCountIn(e.beat, e.length, 1); }, hidden: true),
|
|
new GameAction("4 beat count-in (cowbell)", "", 4f, function: delegate { var e = eventCaller.currentEntity; SoundEffects.FourBeatCountIn(e.beat, e.length, 2); }, hidden: true),
|
|
new GameAction("8 beat count-in (alt)", "", 8f, function: delegate { var e = eventCaller.currentEntity; SoundEffects.EightBeatCountIn(e.beat, e.length, 1); }, hidden: true),
|
|
new GameAction("8 beat count-in (cowbell)", "", 8f, function: delegate { var e = eventCaller.currentEntity; SoundEffects.EightBeatCountIn(e.beat, e.length, 2); }, hidden: true),
|
|
|
|
new GameAction("one", "", function: delegate { SoundEffects.Count(0, false); }, hidden: true),
|
|
new GameAction("two", "", function: delegate { SoundEffects.Count(1, false); }, hidden: true),
|
|
new GameAction("three", "", function: delegate { SoundEffects.Count(2, false); }, hidden: true),
|
|
new GameAction("four", "", function: delegate { SoundEffects.Count(3, false); }, hidden: true),
|
|
new GameAction("one (alt)", "", function: delegate { SoundEffects.Count(0, true); }, hidden: true),
|
|
new GameAction("two (alt)", "", function: delegate { SoundEffects.Count(1, true); }, hidden: true),
|
|
new GameAction("three (alt)", "", function: delegate { SoundEffects.Count(2, true); }, hidden: true),
|
|
new GameAction("four (alt)", "", function: delegate { SoundEffects.Count(3, true); }, hidden: true),
|
|
new GameAction("go! (alt)", "", function: delegate { SoundEffects.Go(true); }, hidden: true),
|
|
}),
|
|
|
|
new Minigame("vfx", "Visual Effects", "", false, true, new List<GameAction>()
|
|
{
|
|
new GameAction("flash", "Flash", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("colorA", Color.white, "Start Color"),
|
|
new Param("colorB", Color.white, "End Color"),
|
|
new Param("valA", new EntityTypes.Float(0, 1, 1), "Start Opacity"),
|
|
new Param("valB", new EntityTypes.Float(0, 1, 0), "End Opacity"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease")
|
|
}
|
|
),
|
|
new GameAction("filter", "Filter", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("filter", Games.Global.Filter.FilterType.grayscale, "Filter"),
|
|
new Param("inten", new EntityTypes.Float(0, 100, 100), "Intensity"),
|
|
new Param("fadein", new EntityTypes.Float(0, 100, 0), "Fade In"),
|
|
new Param("fadeout", new EntityTypes.Float(0, 100, 0), "Fade Out")
|
|
}
|
|
),
|
|
new GameAction("move camera", "Move Camera", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left", "Next position on the X axis"),
|
|
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down", "Next position on the Y axis"),
|
|
new Param("valC", new EntityTypes.Float(-0, 250, 10), "In / Out", "Next position on the Z axis"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type"),
|
|
new Param("axis", GameCamera.CameraAxis.All, "Axis", "The axis to move the camera on" )
|
|
}
|
|
),
|
|
new GameAction("rotate camera", "Rotate Camera", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Integer(-360, 360, 0), "Pitch", "Next rotation on the X axis"),
|
|
new Param("valB", new EntityTypes.Integer(-360, 360, 0), "Yaw", "Next rotation on the Y axis"),
|
|
new Param("valC", new EntityTypes.Integer(-360, 360, 0), "Roll", "Next rotation on the Z axis"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type"),
|
|
new Param("axis", GameCamera.CameraAxis.All, "Axis", "The axis to move the camera on" )
|
|
}
|
|
),
|
|
new GameAction("pan view", "Pan Viewport", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(-50, 50, 0), "Right / Left", "Next position on the X axis"),
|
|
new Param("valB", new EntityTypes.Float(-50, 50, 0), "Up / Down", "Next position on the Y axis"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type"),
|
|
new Param("axis", StaticCamera.ViewAxis.All, "Axis", "The axis to pan the viewport in" )
|
|
}
|
|
),
|
|
new GameAction("rotate view", "Rotate Viewport", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(-360, 360, 0), "Rotation", "Next viewport rotation"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type"),
|
|
}
|
|
),
|
|
new GameAction("scale view", "Scale Viewport", 1f, true, new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(0, 50, 1), "Width", "Next viewport width"),
|
|
new Param("valB", new EntityTypes.Float(0, 50, 1), "Height", "Next viewport height"),
|
|
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease Type"),
|
|
new Param("axis", StaticCamera.ViewAxis.All, "Axis", "The axis to scale the viewport in" )
|
|
}
|
|
),
|
|
|
|
new GameAction("screen shake", "Screen Shake", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("valA", new EntityTypes.Float(0, 10, 0), "Horizontal Intensity"),
|
|
new Param("valB", new EntityTypes.Float(0, 10, 1), "Vertical Intensity")
|
|
}
|
|
),
|
|
|
|
new GameAction("display textbox", "Display Textbox", 1f, true, new List<Param>()
|
|
{
|
|
new Param("text1", "", "Text", "The text to display in the textbox (Rich Text is supported!)"),
|
|
new Param("type", Games.Global.Textbox.TextboxAnchor.TopMiddle, "Anchor", "Where to anchor the textbox"),
|
|
new Param("valA", new EntityTypes.Float(0.25f, 4, 1), "Textbox Width", "Textbox width multiplier"),
|
|
new Param("valB", new EntityTypes.Float(0.5f, 8, 1), "Textbox Height", "Textbox height multiplier")
|
|
}
|
|
),
|
|
new GameAction("display open captions", "Display Open Captions", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("text1", "", "Text", "The text to display in the captions (Rich Text is supported!)"),
|
|
new Param("type", Games.Global.Textbox.TextboxAnchor.BottomMiddle, "Anchor", "Where to anchor the captions"),
|
|
new Param("valA", new EntityTypes.Float(0.25f, 4, 1), "Captions Width", "Captions width multiplier"),
|
|
new Param("valB", new EntityTypes.Float(0.5f, 8, 1), "Captions Height", "Captions height multiplier")
|
|
}
|
|
),
|
|
new GameAction("display closed captions", "Display Closed Captions", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("text1", "", "Text", "The text to display in the captions (Rich Text is supported!)"),
|
|
new Param("type", Games.Global.Textbox.ClosedCaptionsAnchor.Top, "Anchor", "Where to anchor the captions"),
|
|
new Param("valA", new EntityTypes.Float(0.5f, 4, 1), "Captions Height", "Captions height multiplier")
|
|
}
|
|
),
|
|
new GameAction("display song artist", "Display Song Info", 1f, true,
|
|
new List<Param>()
|
|
{
|
|
new Param("text1", "", "Title", "Text to display in the upper label (Rich Text is supported!)"),
|
|
new Param("text2", "", "Artist", "Text to display in the lower label (Rich Text is supported!)"),
|
|
}
|
|
),
|
|
}),
|
|
};
|
|
|
|
BuildLoadRunnerList();
|
|
foreach(var load in loadRunners)
|
|
{
|
|
Debug.Log("Running game loader " + RuntimeReflectionExtensions.GetMethodInfo(load).DeclaringType.Name);
|
|
eventCaller.minigames.Add(load(eventCaller));
|
|
}
|
|
}
|
|
}
|
|
} |