添加对 Core 的直接引用

This commit is contained in:
OLDREDSTONE
2025-03-25 15:49:57 +08:00
parent 24907abedb
commit 06c19bf10a
296 changed files with 24961 additions and 2 deletions
@@ -0,0 +1,122 @@
using RhythmBase.Components;
using RhythmBase.Components.Easing;
using System.Reflection;
using static RhythmBase.Extensions.Extensions;
namespace RhythmBase.Events
{
public partial class CallCustomMethod
{
/// <summary>
/// Contains a series of default custom method implementations.
/// </summary>
public static class Shared
{
#pragma warning disable CS1591
public static CallCustomMethod PropertyAssignment(string propertyName, bool value) => new() { MethodName = $"{propertyName.ToLowerCamelCase()} = {value}" };
public static CallCustomMethod RoomPropertyAssignment(byte room, string propertyName, bool value) => new() { MethodName = $"room[{room}].{propertyName.ToLowerCamelCase()} = {value}" };
public static CallCustomMethod FunctionCalling(string functionName, params object[] @params) => new() { MethodName = $"{functionName}({ArgumentCombining(@params)})" };
private static string ArgumentCombining(params object[] @params) => $"{string.Join(", ", @params.Select(i =>
i.GetType() == typeof(string) || i.GetType().IsEnum
? $"str:{i}"
: i.ToString() ?? ""))}";
public static CallCustomMethod RoomFunctionCalling(byte room, string functionName, params object[] @params) => new() { MethodName = $"room[{room}].{functionName}({ArgumentCombining(@params)})" };
private static CallCustomMethod VfxFunctionCalling(string functionName, params object[] @params) => new() { MethodName = $"vfx.{functionName}({ArgumentCombining(@params)})" };
public static CallCustomMethod SetScoreboardLights(bool Mode, string Text) => FunctionCalling(nameof(SetScoreboardLights), Mode, Text);
public static CallCustomMethod InvisibleChars(bool value) => PropertyAssignment(nameof(InvisibleChars), value);
public static CallCustomMethod InvisibleHeart(bool value) => PropertyAssignment(nameof(InvisibleHeart), value);
public static CallCustomMethod NoHitFlashBorder(bool value) => PropertyAssignment(nameof(NoHitFlashBorder), value);
public static CallCustomMethod NoHitStrips(bool value) => PropertyAssignment(nameof(NoHitStrips), value);
public static CallCustomMethod SetOneshotType(int rowID, ShockWaveType wavetype) => FunctionCalling(nameof(SetOneshotType), rowID, wavetype);
public static CallCustomMethod WobblyLines(bool value) => PropertyAssignment(nameof(WobblyLines), value);
public static CallCustomMethod ShockwaveSizeMultiplier(bool value) => PropertyAssignment(nameof(ShockwaveSizeMultiplier), value);
public static CallCustomMethod ShockwaveDistortionMultiplier(bool value) => PropertyAssignment(nameof(ShockwaveDistortionMultiplier), value);
public static CallCustomMethod ShockwaveDurationMultiplier(bool value) => PropertyAssignment(nameof(ShockwaveDurationMultiplier), value);
public static CallCustomMethod MistakeOrHeal(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHeal), damageOrHeal);
public static CallCustomMethod MistakeOrHealP1(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHealP1), damageOrHeal);
public static CallCustomMethod MistakeOrHealP2(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHealP2), damageOrHeal);
public static CallCustomMethod MistakeOrHealSilent(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHealSilent), damageOrHeal);
public static CallCustomMethod MistakeOrHealP1Silent(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHealP1Silent), damageOrHeal);
public static CallCustomMethod MistakeOrHealP2Silent(float damageOrHeal) => FunctionCalling(nameof(MistakeOrHealP2Silent), damageOrHeal);
public static CallCustomMethod SetMistakeWeight(int rowID, float weight) => FunctionCalling(nameof(SetMistakeWeight), rowID, weight);
public static CallCustomMethod DamageHeart(int rowID, float damage) => FunctionCalling(nameof(DamageHeart), rowID, damage);
public static CallCustomMethod HealHeart(int rowID, float damage) => FunctionCalling(nameof(HealHeart), rowID, damage);
public static CallCustomMethod WavyRowsAmplitude(byte roomID, float amplitude) => RoomPropertyAssignment(roomID, nameof(WavyRowsAmplitude), amplitude != 0f);
public static CallCustomMethod WavyRowsFrequency(byte roomID, float frequency) => RoomPropertyAssignment(roomID, nameof(WavyRowsFrequency), frequency != 0f);
public static CallCustomMethod SetShakeIntensityOnHit(byte roomID, int number, int strength) => RoomFunctionCalling(roomID, nameof(SetShakeIntensityOnHit), number, strength);
public static CallCustomMethod ShowPlayerHand(byte roomID, bool isPlayer1, bool isShortArm, bool isInstant) => FunctionCalling(nameof(ShowPlayerHand), roomID, isPlayer1, isShortArm, isInstant);
public static CallCustomMethod TintHandsWithInts(byte roomID, float R, float G, float B, float A) => FunctionCalling(nameof(TintHandsWithInts), roomID, R, G, B, A);
public static CallCustomMethod SetHandsBorderColor(byte roomID, float R, float G, float B, float A, int style) => FunctionCalling(nameof(SetHandsBorderColor), roomID, R, G, B, A, style);
public static CallCustomMethod SetAllHandsBorderColor(float R, float G, float B, float A, int style) => FunctionCalling(nameof(SetAllHandsBorderColor), R, G, B, A, style);
public static CallCustomMethod SetHandToP1(int room, bool rightHand) => FunctionCalling(nameof(SetHandToP1), room, rightHand);
public static CallCustomMethod SetHandToP2(int room, bool rightHand) => FunctionCalling(nameof(SetHandToP2), room, rightHand);
public static CallCustomMethod SetHandToIan(int room, bool rightHand) => FunctionCalling(nameof(SetHandToIan), room, rightHand);
public static CallCustomMethod SetHandToPaige(int room, bool rightHand) => FunctionCalling(nameof(SetHandToPaige), room, rightHand);
public static CallCustomMethod SetShadowRow(int mimickerRowID, int mimickedRowID) => FunctionCalling(nameof(SetShadowRow), mimickerRowID, mimickedRowID);
public static CallCustomMethod UnsetShadowRow(int mimickerRowID, int mimickedRowID) => FunctionCalling(nameof(UnsetShadowRow), mimickerRowID, mimickedRowID);
public static CallCustomMethod ShakeCam(int number, int strength, int roomID) => VfxFunctionCalling(nameof(ShakeCam), number, strength, roomID);
public static CallCustomMethod StopShakeCam(int roomID) => VfxFunctionCalling(nameof(StopShakeCam), roomID);
public static CallCustomMethod ShakeCamSmooth(int duration, int strength, int roomID) => VfxFunctionCalling(nameof(ShakeCamSmooth), duration, strength, roomID);
public static CallCustomMethod ShakeCamRotate(int duration, int strength, int roomID) => VfxFunctionCalling(nameof(ShakeCamRotate), duration, strength, roomID);
public static CallCustomMethod SetKaleidoscopeColor(int roomID, float R1, float G1, float B1, float R2, float G2, float B2) => FunctionCalling(nameof(SetKaleidoscopeColor), roomID, R1, G1, B1, R2, G2, B2);
public static CallCustomMethod SyncKaleidoscopes(int targetRoomID, int otherRoomID) => FunctionCalling(nameof(SyncKaleidoscopes), targetRoomID, otherRoomID);
public static CallCustomMethod SetVignetteAlpha(float alpha, int roomID) => VfxFunctionCalling(nameof(SetVignetteAlpha), alpha, roomID);
public static CallCustomMethod NoOneshotShadows(bool value) => PropertyAssignment(nameof(NoOneshotShadows), value);
public static CallCustomMethod EnableRowReflections(int roomID) => FunctionCalling(nameof(EnableRowReflections), roomID);
public static CallCustomMethod DisableRowReflections(int roomID) => FunctionCalling(nameof(DisableRowReflections), roomID);
public static CallCustomMethod ChangeCharacter(string Name, int roomID) => FunctionCalling(nameof(ChangeCharacter), Name, roomID);
public static CallCustomMethod ChangeCharacter(RDCharacters Name, int roomID) => FunctionCalling(nameof(ChangeCharacter), Name, roomID);
public static CallCustomMethod ChangeCharacterSmooth(string Name, int roomID) => FunctionCalling(nameof(ChangeCharacterSmooth), Name, roomID);
public static CallCustomMethod ChangeCharacterSmooth(RDCharacters Name, int roomID) => FunctionCalling(nameof(ChangeCharacterSmooth), Name, roomID);
public static CallCustomMethod SmoothShake(bool value) => PropertyAssignment(nameof(SmoothShake), value);
public static CallCustomMethod RotateShake(bool value) => PropertyAssignment(nameof(RotateShake), value);
public static CallCustomMethod DisableRowChangeWarningFlashes(bool value) => PropertyAssignment(nameof(DisableRowChangeWarningFlashes), value);
public static CallCustomMethod StatusSignWidth(float value) => PropertyAssignment(nameof(StatusSignWidth), value != 0f);
public static CallCustomMethod SkippableRankScreen(bool value) => PropertyAssignment(nameof(SkippableRankScreen), value);
public static CallCustomMethod MissesToCrackHeart(int value) => PropertyAssignment(nameof(MissesToCrackHeart), value != 0);
public static CallCustomMethod SkipRankText(bool value) => PropertyAssignment(nameof(SkipRankText), value);
public static CallCustomMethod AlternativeMatrix(bool value) => PropertyAssignment(nameof(AlternativeMatrix), value);
public static CallCustomMethod ToggleSingleRowReflections(byte room, byte row, bool value) => FunctionCalling(nameof(ToggleSingleRowReflections), room, row, value);
public static CallCustomMethod SetScrollSpeed(byte roomID, float speed, float duration, EaseType ease) => RoomFunctionCalling(roomID, nameof(SetScrollSpeed), speed, duration, ease);
public static CallCustomMethod SetScrollOffset(byte roomID, float cameraOffset, float duration, EaseType ease) => RoomFunctionCalling(roomID, nameof(SetScrollOffset), cameraOffset, duration, ease);
public static CallCustomMethod DarkenedRollerdisco(byte roomID, float value) => RoomFunctionCalling(roomID, nameof(DarkenedRollerdisco), value);
public static CallCustomMethod CurrentSongVol(float targetVolume, float fadeTimeSeconds) => FunctionCalling(nameof(CurrentSongVol), targetVolume, fadeTimeSeconds);
public static CallCustomMethod PreviousSongVol(float targetVolume, float fadeTimeSeconds) => FunctionCalling(nameof(PreviousSongVol), targetVolume, fadeTimeSeconds);
public static CallCustomMethod EditTree(byte room, string property, float value, float beats, EaseType ease) => RoomFunctionCalling(room, nameof(EditTree), property, value, beats, ease);
public static IEnumerable<CallCustomMethod> EditTree(byte room, ProceduralTree treeProperties, float beats, EaseType ease)
{
List<CallCustomMethod> L = [];
foreach (FieldInfo p in typeof(ProceduralTree).GetFields())
{
float? value = (float?)p.GetValue(treeProperties);
if (value != null)
L.Add(EditTree(room, p.Name.ToLowerCamelCase(), (float)value, beats, ease));
}
return L;
}
public static CallCustomMethod EditTreeColor(byte room, bool location, string color, float beats, EaseType ease) => RoomFunctionCalling(room, nameof(EditTreeColor), location, color, beats, ease);
public struct ProceduralTree
{
/// <summary>
/// 过程树的属性
/// </summary>
public float?
BrachedPerlteration,
BranchesPerDivision,
IterationsPerSecond,
Thickness,
TargetLength,
MaxDeviation,
Angle,
CamAngle,
CamDistance,
CamDegreesPerSecond,
CamSpeed,
PulseIntensity,
PulseRate,
PulseWavelength;
}
}
}
}
+22
View File
@@ -0,0 +1,22 @@
using RhythmBase.Components;
using RhythmBase.Components.Easing;
using static RhythmBase.Extensions.Extensions;
namespace RhythmBase.Events
{
public partial class Comment
{
/// <summary>
/// Contains a series of default custom method implementations.
/// </summary>
public static class Shared
{
#pragma warning disable CS1591
private static Comment FunctionCalling(string name, params object[] @params) => new() { Text = $"()=>{name.ToLowerCamelCase()}({string.Join(',', @params.Select(i => i.ToString()))})" };
public static Comment TrueCameraMove(int RoomID, RDPointN p, float AnimationDuration, EaseType Ease) => FunctionCalling("TrueCameraMove", (byte)RoomID, p.X, p.Y, AnimationDuration, Ease);
public static Comment Create(Particle particleName, RDPointN p) => FunctionCalling("Create", $"CustomParticles/{particleName}", p.X, p.Y);
public static Comment Shockwave(ShockWaveType type, float value) => FunctionCalling("Shockwave", type, value);
public static Comment WavyRowsAmplitude(byte roomID, float amplitude, float duration) => FunctionCalling("WavyRowsAmplitude", roomID, amplitude, duration);
}
}
}
@@ -0,0 +1,55 @@
using RhythmBase.Components;
using RhythmBase.Components.Easing;
using RhythmBase.Events;
using System.Reflection;
namespace RhythmBase.Extensions
{
/// <summary>
/// Provides extension methods for retrieving easing properties from events.
/// </summary>
public static class EasePropertyExtensions
{
private static readonly Dictionary<Type, PropertyInfo[]> EaseProperties = [];
/// <summary>
/// Retrieves the easing properties of the specified type of event.
/// </summary>
/// <typeparam name="TEvent">The type of the event that implements <see cref="IEaseEvent"/>.</typeparam>
/// <param name="obj">The collection of events to retrieve the easing properties from.</param>
/// <returns>A dictionary containing the names and corresponding easing properties of the specified event type.</returns>
/// <exception cref="NotSupportedException">Thrown when an unsupported property type is encountered.</exception>
public static Dictionary<string, IEaseProperty> GetEaseProperties<TEvent>(this IEnumerable<TEvent> obj) where TEvent : IEaseEvent, new()
{
if (!EaseProperties.TryGetValue(typeof(TEvent), out PropertyInfo[]? properties))
{
EaseProperties[obj.GetType()] = properties = typeof(TEvent).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.GetCustomAttribute<EasePropertyAttribute>() != null)
.ToArray();
}
Dictionary<string, IEaseProperty> values = [];
foreach (var property in properties)
{
if (property.PropertyType.IsAssignableFrom(typeof(RDExpression)))
{
values[property.Name] = EasePropertyFloat.CreateEaseProperty(0, [.. obj], property);
}
else if (property.PropertyType.IsAssignableFrom(typeof(RDPointE)))
{
values[property.Name] = EasePropertyPoint.CreateEaseProperty(default, [.. obj], property);
}
else if (property.PropertyType.IsAssignableFrom(typeof(RDSizeE)))
{
values[property.Name] = EasePropertySize.CreateEaseProperty(default, [.. obj], property);
}
else if (property.PropertyType.IsAssignableFrom(typeof(RDColor)))
{
values[property.Name] = EasePropertyColor.CreateEaseProperty(default, [.. obj], property);
}
else
{
throw new NotSupportedException($"Unsupported property type {property.PropertyType}");
}
}
return values;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,587 @@
using RhythmBase.Components;
using RhythmBase.Components.Easing;
using RhythmBase.Events;
using RhythmBase.Exceptions;
using RhythmBase.Extensions;
using System.Text.RegularExpressions;
namespace RhythmBase.Extensions
{
public static partial class Extensions
{
/// <summary>
/// Get the pulse sound effect of row beat event.
/// </summary>
/// <returns>The sound effect of row beat event.</returns>
public static RDAudio BeatSound(this BaseBeat e)
{
SetBeatSound? setBeatSound = e.Parent?.LastOrDefault((SetBeatSound i) => i.Beat < e.Beat && i.Active);
return (setBeatSound?.Sound) ?? e.Parent?.Sound ?? throw new NotImplementedException();
}
/// <summary>
/// Getting controlled events.
/// </summary>
public static IEnumerable<IGrouping<string, IBaseEvent>> ControllingEvents(this TagAction e) => e.Beat.BaseLevel?.GetTaggedEvents(e.ActionTag, e.Action.HasFlag(TagAction.Actions.All)) ?? [];
/// <summary>
/// Creates a new <see cref="T:RhythmBase.Events.AdvanceText" /> subordinate to <see cref="T:RhythmBase.Events.FloatingText" /> at the specified beat. The new event created will be attempted to be added to the <see cref="T:RhythmBase.Events.FloatingText" />'s source level.
/// </summary>
/// <param name="e">RDLevel</param>
/// <param name="beat">Specified beat.</param>
public static AdvanceText CreateAdvanceText(this FloatingText e, RDBeat beat)
{
AdvanceText A = new()
{
Parent = e,
Beat = beat.WithoutBinding()
};
e.Children.Add(A);
return A;
}
/// <summary>
/// Get the end beat of the duration.
/// </summary>
/// <param name="beat"></param>
/// <param name="duration"></param>
/// <returns></returns>
/// <exception cref="InvalidRDBeatException"></exception>
public static RDBeat DurationOffset(this RDBeat beat, float duration)
{
SetBeatsPerMinute setBPM = beat.BaseLevel?.First((SetBeatsPerMinute i) => i.Beat > beat) ?? throw new InvalidRDBeatException();
RDBeat DurationOffset =
beat.BarBeat.bar == setBPM.Beat.BarBeat.bar
? beat + duration
: beat + TimeSpan.FromMinutes(duration / beat.BPM);
return DurationOffset;
}
/// <summary>
/// Returns the pulse beat of the specified 0-based index.
/// </summary>
/// <exception cref="T:RhythmBase.Exceptions.RhythmBaseException">THIS IS 7TH BEAT GAMES!</exception>
public static RDBeat GetBeat(this AddClassicBeat e, byte index)
{
SetRowXs x = e.Parent?.LastOrDefault((SetRowXs i) => i.Active && e.IsBehind(i)) ?? new();
float Synco = 0 <= x.SyncoBeat && x.SyncoBeat < (sbyte)index ? (float)((x.SyncoSwing == 0f) ? 0.5 : ((double)x.SyncoSwing)) : 0f;
if (index >= 7)
throw new RhythmBaseException("THIS IS 7TH BEAT GAMES!");
return e.Beat.DurationOffset(e.Tick * ((float)index - Synco));
}
/// <summary>
/// Converts Xs patterns to string form.
/// </summary>
public static string GetPatternString(this SetRowXs e) => Utils.Utils.GetPatternString(e.Pattern);
/// <summary>
/// Get the sequence of <see cref="T:RhythmBase.Events.PulseFreeTimeBeat" /> belonging to this <see cref="T:RhythmBase.Events.AddFreeTimeBeat" />, return all of the <see cref="T:RhythmBase.Events.PulseFreeTimeBeat" /> from the time the pulse was created to the time it was removed or hit.
/// </summary>
public static IEnumerable<PulseFreeTimeBeat> GetPulses(this AddFreeTimeBeat e)
{
List<PulseFreeTimeBeat> Result = [];
byte pulse = e.Pulse;
if (e.Parent == null)
yield break;
foreach (PulseFreeTimeBeat item in e.Parent.Where<PulseFreeTimeBeat>(i => i.Active && e.IsInFrontOf(i)))
{
switch (item.Action)
{
case PulseFreeTimeBeat.ActionType.Increment:
pulse += 1;
yield return item;
break;
case PulseFreeTimeBeat.ActionType.Decrement:
pulse = (byte)((pulse > 0b1) ? (pulse - 0b1) : 0b1);
yield return item;
break;
case PulseFreeTimeBeat.ActionType.Custom:
pulse = (byte)item.CustomPulse;
yield return item;
break;
case PulseFreeTimeBeat.ActionType.Remove:
yield return item;
break;
}
if (pulse == 6)
break;
}
}
/// <summary>
/// Get the hit sound effect of row beat event.
/// </summary>
/// <returns>The sound effect of row beat event.</returns>
public static RDAudio HitSound(this BaseBeat e)
{
RDAudio DefaultAudio = new()
{
Filename = "sndClapHit",
Offset = TimeSpan.Zero,
Pan = 100,
Pitch = 100,
Volume = 100
};
RDAudio? HitSound;
switch (e.Player())
{
case PlayerType.P1:
{
SetClapSounds? setClapSounds = e.Beat.BaseLevel?.LastOrDefault((SetClapSounds i) => i.Active && i.P1Sound != null);
HitSound = (setClapSounds?.P1Sound) ?? DefaultAudio;
break;
}
case PlayerType.P2:
{
SetClapSounds? setClapSounds2 = e.Beat.BaseLevel?.LastOrDefault((SetClapSounds i) => i.Active && i.P2Sound != null);
HitSound = (setClapSounds2?.P2Sound) ?? DefaultAudio;
break;
}
case PlayerType.CPU:
{
SetClapSounds? setClapSounds3 = e.Beat.BaseLevel?.LastOrDefault((SetClapSounds i) => i.Active && i.CpuSound != null);
HitSound = (setClapSounds3?.CpuSound) ?? DefaultAudio;
break;
}
default:
HitSound = DefaultAudio;
break;
}
return HitSound;
}
/// <summary>
/// Get all hits.
/// </summary>
public static IEnumerable<RDHit> HitTimes(this AddClassicBeat e) =>
new List<RDHit> { new(e, e.GetBeat(6), e.Hold) }
.AsEnumerable();
/// <summary>
/// Get all hits.
/// </summary>
public static IEnumerable<RDHit> HitTimes(this AddOneshotBeat e)
{
e._beat.IfNullThrowException();
List<RDHit> L = [];
uint loops = e.Loops;
for (uint i = 0U; i <= loops; i += 1U)
{
sbyte b = (sbyte)(e.Subdivisions - 1);
for (sbyte j = 0; j <= b; j += 1)
L.Add(new RDHit(e, new RDBeat(e._beat._calculator, e._beat.BeatOnly + i * e.Interval + e.Tick + e.Delay + (float)j * (e.Tick / (float)e.Subdivisions)), 0f));
}
return L.AsEnumerable();
}
/// <summary>
/// Get all hits.
/// </summary>
public static IEnumerable<RDHit> HitTimes(this AddFreeTimeBeat e) =>
e.Pulse == 6
? [new(e, e.Beat, e.Hold)]
: ([]);
/// <summary>
/// Get all hits.
/// </summary>
public static IEnumerable<RDHit> HitTimes(this PulseFreeTimeBeat e) =>
e.IsHitable()
? [new(e, e.Beat, e.Hold)]
: ([]);
/// <summary>
/// Get all hits.
/// </summary>
public static IEnumerable<RDHit> HitTimes(this BaseBeat e) => e.Type switch
{
EventType.AddClassicBeat => ((AddClassicBeat)e).HitTimes(),
EventType.AddFreeTimeBeat => ((AddFreeTimeBeat)e).HitTimes(),
EventType.AddOneshotBeat => ((AddOneshotBeat)e).HitTimes(),
_ => e.Type != EventType.PulseFreeTimeBeat
? Array.Empty<RDHit>().AsEnumerable()
: ((PulseFreeTimeBeat)e).HitTimes(),
};
/// <summary>
/// Determine if <paramref name="item1" /> is after <paramref name="item2" />
/// </summary>
/// <returns><list type="table">
/// <item>If <paramref name="item1" /> is after <paramref name="item2" />, <see langword="true" /></item>
/// <item>Else, <see langword="false" /></item>
/// </list></returns>
public static bool IsBehind(this OrderedEventCollection e, IBaseEvent item1, IBaseEvent item2) => (item1.Beat > item2.Beat || (item1.Beat.BeatOnly == item2.Beat.BeatOnly && (e.eventsBeatOrder[item1.Beat].BeforeThan(item2, item1))));
/// <summary>
/// Check if another event is after itself, including events of the same beat but executed after itself.
/// </summary>
public static bool IsBehind(this IBaseEvent e, IBaseEvent item) => e.Beat.BaseLevel?.IsBehind(e, item) ?? throw new InvalidRDBeatException();
/// <summary>
/// Check if it can be hit by player.
/// </summary>
public static bool IsHitable(this PulseFreeTimeBeat e)
{
int PulseIndexMin = 6;
int PulseIndexMax = 6;
if (e.Parent is null)
return false;
foreach (BaseBeat item in ((IEnumerable<BaseBeat>)e.Parent
.Where(e.IsBehind, new RDRange(e.Beat, null)))
.Reverse())
{
EventType type = item.Type;
switch (type)
{
case EventType.AddFreeTimeBeat:
{
AddFreeTimeBeat Temp2 = (AddFreeTimeBeat)item;
if (PulseIndexMin <= (int)Temp2.Pulse & (int)Temp2.Pulse <= PulseIndexMax)
return true;
break;
}
case EventType.PulseFreeTimeBeat:
{
PulseFreeTimeBeat Temp = (PulseFreeTimeBeat)item;
switch (Temp.Action)
{
case PulseFreeTimeBeat.ActionType.Increment:
if (PulseIndexMin > 0)
PulseIndexMin--;
if (!(PulseIndexMax > 0))
return false;
PulseIndexMax--;
break;
case PulseFreeTimeBeat.ActionType.Decrement:
if (PulseIndexMin > 0)
PulseIndexMin++;
if (!(PulseIndexMax < 6))
return false;
PulseIndexMax++;
break;
case PulseFreeTimeBeat.ActionType.Custom:
if (!(PulseIndexMin <= Temp.CustomPulse & Temp.CustomPulse <= PulseIndexMax))
return false;
PulseIndexMin = 0;
PulseIndexMax = 5;
break;
case PulseFreeTimeBeat.ActionType.Remove:
return false;
}
if (PulseIndexMin > PulseIndexMax)
return false;
break;
}
}
}
return false;
}
/// <summary>
/// Check if it can be hit by player.
/// </summary>
public static bool IsHitable(this AddFreeTimeBeat e) => e.Pulse == 6;
/// <summary>
/// Check if it can be hit by player.
/// </summary>
public static bool IsHitable(this BaseBeat e) => e.Type switch
{
EventType.AddClassicBeat or EventType.AddOneshotBeat => true,
EventType.AddFreeTimeBeat => ((AddFreeTimeBeat)e).IsHitable(),
_ => e.Type == EventType.PulseFreeTimeBeat && ((PulseFreeTimeBeat)e).IsHitable(),
};
/// <summary>
/// Determine if <paramref name="item1" /> is in front of <paramref name="item2" />
/// </summary>
/// <returns><list type="table">
/// <item>If <paramref name="item1" /> is in front of <paramref name="item2" />, <see langword="true" /></item>
/// <item>Else, <see langword="false" /></item>
/// </list></returns>
public static bool IsInFrontOf(this OrderedEventCollection e, IBaseEvent item1, IBaseEvent item2) => (item1.Beat < item2.Beat || (item1.Beat.BeatOnly == item2.Beat.BeatOnly && (e.eventsBeatOrder[item1.Beat].BeforeThan(item1, item2))));
/// <summary>
/// Check if another event is in front of itself, including events of the same beat but executed before itself.
/// </summary>
public static bool IsInFrontOf(this IBaseEvent e, IBaseEvent item) => e.Beat.BaseLevel?.IsInFrontOf(e, item) ?? throw new InvalidRDBeatException();
/// <summary>
/// Get the total length of the oneshot.
/// </summary>
/// <returns></returns>
public static float Length(this AddOneshotBeat e) => e.Tick * e.Loops + e.Interval * e.Loops - 1f;
/// <summary>
/// Get the total length of the classic beat.
/// </summary>
/// <returns></returns>
public static float Length(this AddClassicBeat e)
{
float SyncoSwing = e.Parent?.LastOrDefault((SetRowXs i) => i.Active && e.IsBehind(i))?.SyncoSwing ?? 0;
return (float)((double)(e.Tick * 6f) - ((SyncoSwing == 0f) ? 0.5 : ((double)SyncoSwing)) * (double)e.Tick);
}
/// <summary>
/// Specifies the position of the image. This method changes both the pivot and the angle to keep the image visually in its original position.
/// </summary>
/// <param name="spriteSize">Sprite size. </param>
/// <param name="e">RDLevel</param>
/// <param name="target">Specified position. </param>
public static void MovePositionMaintainVisual(this Move e, RDSizeE spriteSize, RDPointE target)
{
if (e.Position != null && e.Pivot != null && e.Angle != null && e.Angle.Value.IsNumeric)
{
e.Position = new RDPointE?(target);
e.Pivot = new RDPointE?((e.VisualPosition(spriteSize) - new RDSizeE(target)).Rotate(e.Angle.Value.NumericValue));
}
}
/// <summary>
/// Specifies the position of the image. This method changes both the pivot and the angle to keep the image visually in its original position.
/// </summary>
/// <param name="e">RDLevel</param>
/// <param name="target">Specified position. </param>
public static void MovePositionMaintainVisual(this MoveRoom e, RDSizeE target)
{
if (e.RoomPosition != null && e.Pivot != null && e.Angle != null && e.Angle.Value.IsNumeric)
{
e.RoomPosition = new RDPointE?((RDPointE)target);
e.Pivot = new RDPointE?((e.VisualPosition() - new RDSizeE((RDPointE)target)).Rotate(e.Angle.Value.NumericValue));
}
}
/// <summary>
/// Convert beat pattern to string.
/// </summary>
/// <returns>The pattern string.</returns>
public static string Pattern(this AddClassicBeat e) => Utils.Utils.GetPatternString(e.RowXs());
/// <summary>
/// Get current player of the beat event.
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public static PlayerType Player(this BaseBeat e)
{
ChangePlayersRows? changePlayersRows = e.Beat.BaseLevel?.LastOrDefault((ChangePlayersRows i) => i.Active && i.Players[e.Index] != PlayerType.NoChange);
return (changePlayersRows != null)
? changePlayersRows.Players[e.Index]
: (PlayerType)(e.Parent?.Player ?? throw new NotImplementedException());
}
/// <summary>
/// Get the actual beat pattern.
/// </summary>
/// <returns>The actual beat pattern.</returns>
public static Patterns[] RowXs(this AddClassicBeat e)
{
if (e.SetXs == null)
{
SetRowXs X = e.Parent?.LastOrDefault((SetRowXs i) => i.Active && e.IsBehind(i)) ?? new();
return X.Pattern;
}
else
{
Patterns[] T = new Patterns[6];
AddClassicBeat.ClassicBeatPatterns? setXs = e.SetXs;
int? num = (setXs != null) ? new int?((int)setXs.GetValueOrDefault()) : null;
if (((num != null) ? new bool?(num.GetValueOrDefault() == 0) : null).GetValueOrDefault())
{
T[1] = Patterns.X;
T[2] = Patterns.X;
T[4] = Patterns.X;
T[5] = Patterns.X;
}
else
{
num = (setXs != null) ? new int?((int)setXs.GetValueOrDefault()) : null;
if (!(((num != null) ? new bool?(num.GetValueOrDefault() == 1) : null).GetValueOrDefault()))
{
throw new RhythmBaseException("How?");
}
T[1] = Patterns.X;
T[3] = Patterns.X;
T[5] = Patterns.X;
}
return T;
}
}
/// <summary>
/// Get the special tag of the tag event.
/// </summary>
/// <returns>special tags.</returns>
public static TagAction.SpecialTag[] SpetialTags(this TagAction e) => (TagAction.SpecialTag[])(from i in Enum.GetValues<TagAction.SpecialTag>()
where e.ActionTag.Contains(string.Format("[{0}]", i))
select i);
/// <summary>
/// Generate split event instances.
/// </summary>
public static IEnumerable<SayReadyGetSetGo> Split(this SayReadyGetSetGo e) => e.PhraseToSay switch
{
SayReadyGetSetGo.Words.SayReaDyGetSetGoNew => [
e.SplitCopy(0f, SayReadyGetSetGo.Words.JustSayRea),
e.SplitCopy(e.Tick, SayReadyGetSetGo.Words.JustSayDy),
e.SplitCopy(e.Tick * 2f, SayReadyGetSetGo.Words.JustSayGet),
e.SplitCopy(e.Tick * 3f, SayReadyGetSetGo.Words.JustSaySet),
e.SplitCopy(e.Tick * 4f, SayReadyGetSetGo.Words.JustSayGo)
],
SayReadyGetSetGo.Words.SayGetSetGo => [
e.SplitCopy(0f, SayReadyGetSetGo.Words.JustSayGet),
e.SplitCopy(e.Tick, SayReadyGetSetGo.Words.JustSaySet),
e.SplitCopy(e.Tick * 2f, SayReadyGetSetGo.Words.JustSayGo)
],
SayReadyGetSetGo.Words.SayReaDyGetSetOne => [
e.SplitCopy(0f, SayReadyGetSetGo.Words.JustSayRea),
e.SplitCopy(e.Tick, SayReadyGetSetGo.Words.JustSayDy),
e.SplitCopy(e.Tick * 2f, SayReadyGetSetGo.Words.JustSayGet),
e.SplitCopy(e.Tick * 3f, SayReadyGetSetGo.Words.JustSaySet),
e.SplitCopy(e.Tick * 4f, SayReadyGetSetGo.Words.Count1)
],
SayReadyGetSetGo.Words.SayGetSetOne => [
e.SplitCopy(0f, SayReadyGetSetGo.Words.JustSayGet),
e.SplitCopy(e.Tick, SayReadyGetSetGo.Words.JustSaySet),
e.SplitCopy(e.Tick * 2f, SayReadyGetSetGo.Words.Count1)
],
SayReadyGetSetGo.Words.SayReadyGetSetGo => [
e.SplitCopy(0f, SayReadyGetSetGo.Words.JustSayReady),
e.SplitCopy(e.Tick * 2f, SayReadyGetSetGo.Words.JustSayGet),
e.SplitCopy(e.Tick * 3f, SayReadyGetSetGo.Words.JustSaySet),
e.SplitCopy(e.Tick * 4f, SayReadyGetSetGo.Words.JustSayGo)
],
_ => [e],
};
/// <summary>
/// Generate split event instances.
/// </summary>
public static IEnumerable<AddOneshotBeat> Split(this AddOneshotBeat e)
{
e._beat.IfNullThrowException();
List<AddOneshotBeat> L = [];
uint loops = e.Loops;
for (uint i = 0U; i <= loops; i += 1U)
{
AddOneshotBeat T = e.MemberwiseClone();
T.Loops = 0U;
T.Interval = 0f;
T.Beat = new RDBeat(e._beat._calculator, unchecked(e.Beat.BeatOnly + i * e.Interval));
L.Add(T);
}
return L.AsEnumerable();
}
/// <summary>
/// Generate split event instances. Follow the most recently activated Xs.
/// </summary>
public static IEnumerable<BaseBeat> Split(this AddClassicBeat e)
{
SetRowXs x = e.Parent?.LastOrDefault((SetRowXs i) => i.Active && e.IsBehind(i)) ?? new();
return e.Split(x);
}
/// <summary>
/// Generate split event instances.
/// </summary>
public static IEnumerable<BaseBeat> Split(this AddClassicBeat e, SetRowXs Xs)
{
List<BaseBeat> L = [];
AddFreeTimeBeat Head = e.Clone<AddFreeTimeBeat>();
Head.Pulse = 0;
Head.Hold = e.Hold;
L.Add(Head);
int i = 1;
do
{
if (!(i < 6 && Xs.Pattern[i] == Patterns.X))
{
PulseFreeTimeBeat Pulse = e.Clone<PulseFreeTimeBeat>();
PulseFreeTimeBeat pulseFreeTimeBeat;
(pulseFreeTimeBeat = Pulse).Beat = pulseFreeTimeBeat.Beat + e.Tick * (float)i;
if (i >= (int)Xs.SyncoBeat)
(pulseFreeTimeBeat = Pulse).Beat = pulseFreeTimeBeat.Beat - Xs.SyncoSwing;
if (i % 2 == 1)
(pulseFreeTimeBeat = Pulse).Beat = pulseFreeTimeBeat.Beat + (e.Tick - ((e.Swing == 0f) ? e.Tick : e.Swing));
Pulse.Hold = e.Hold;
Pulse.Action = PulseFreeTimeBeat.ActionType.Increment;
L.Add(Pulse);
}
i++;
}
while (i <= 6);
return L.AsEnumerable();
}
/// <summary>
/// Calculates the duration of the VFX effect for the given preset.
/// </summary>
/// <param name="e">The SetVFXPreset event.</param>
/// <returns>An RDRange representing the duration of the VFX effect.</returns>
public static RDRange VFXDuration(this SetVFXPreset e)
{
if (e.Preset != SetVFXPreset.Presets.DisableAll && e.Enable)
{
SetVFXPreset? close = e.After().FirstOrDefault(i =>
i.Rooms.Contains(e.Rooms) && (
i.Preset == e.Preset ||
i.Preset == SetVFXPreset.Presets.DisableAll
));
return new(e.Beat, close?.Beat);
}
return new(e.Beat, e.Beat);
}
/// <summary>
/// Remove auxiliary symbols.
/// </summary>
public static string TextOnly(this ShowDialogue e)
{
string result = e.Text;
foreach (string item in new string[]
{
"shake",
"shakeRadius=\\d+",
"wave",
"waveHeight=\\d+",
"waveSpeed=\\d+",
"swirl",
"swirlRadius=\\d+",
"swirlSpeed=\\d+",
"static"
})
result = Regex.Replace(result, string.Format("\\[{0}\\]", item), "");
return result;
}
/// <summary>
/// The visual position of the lower left corner of the image.
/// </summary>
public static RDPointE VisualPosition(this Move e, RDSizeE spriteSize)
{
RDPointE VisualPosition = default;
if (e.Position != null && e.Pivot != null && e.Angle != null && e.Angle.Value.IsNumeric && e.Scale != null)
{
RDPointE previousPosition = e.Position.Value;
RDExpression? x = e.Pivot?.X * (e.Scale?.Width) * spriteSize.Width / 100f;
RDPointE previousPivot = new(x, e.Pivot?.Y * (e.Scale?.Height) * spriteSize.Height / 100f);
VisualPosition = previousPosition + new RDSizeE(previousPivot.Rotate(e.Angle.Value.NumericValue));
}
return VisualPosition;
}
/// <summary>
/// The visual position of the lower left corner of the image.
/// </summary>
public static RDPointE VisualPosition(this MoveRoom e)
{
RDPointE VisualPosition = default;
if (e.RoomPosition != null && e.Pivot != null && e.Angle != null)
{
RDPointE previousPosition = e.RoomPosition.Value;
RDPointE previousPivot = new((e.Pivot?.X) * (e.Scale?.Width), (e.Pivot?.Y) * (e.Scale?.Height));
VisualPosition = previousPosition + new RDSizeE(previousPivot.Rotate(e.Angle.Value.NumericValue));
}
return VisualPosition;
}
/// <summary>
/// Creates a rotated rectangle for the MoveCamera event.
/// </summary>
/// <param name="e">The MoveCamera event.</param>
/// <returns>A rotated rectangle representing the camera's position, zoom, and angle.</returns>
public static RDRotatedRectE RotatedRect(this MoveCamera e) => new(e.CameraPosition, new(e.Zoom, e.Zoom), null, e.Angle);
/// <summary>
/// Creates a rotated rectangle for the MoveRow event.
/// </summary>
/// <param name="e">The MoveRow event.</param>
/// <returns>A rotated rectangle representing the row's position, scale, pivot, and angle.</returns>
public static RDRotatedRectE RotatedRect(this MoveRow e) => new(e.RowPosition, e.Scale, new(e.Pivot, e.Pivot), e.Angle);
/// <summary>
/// Creates a rotated rectangle for the MoveRoom event.
/// </summary>
/// <param name="e">The MoveRoom event.</param>
/// <returns>A rotated rectangle representing the room's position, scale, pivot, and angle.</returns>
public static RDRotatedRectE RotatedRect(this MoveRoom e) => new(e.RoomPosition, e.Scale, e.Pivot, e.Angle);
/// <summary>
/// Creates a rotated rectangle for the Move event.
/// </summary>
/// <param name="e">The Move event.</param>
/// <returns>A rotated rectangle representing the position, scale, pivot, and angle.</returns>
public static RDRotatedRectE RotatedRect(this Move e) => new(e.Position, e.Scale, e.Pivot, e.Angle);
private static SayReadyGetSetGo SplitCopy(this SayReadyGetSetGo e, float extraBeat, SayReadyGetSetGo.Words word)
{
SayReadyGetSetGo Temp = e.Clone<SayReadyGetSetGo>();
Temp.Beat += extraBeat;
Temp.PhraseToSay = word;
Temp.Volume = e.Volume;
return Temp;
}
}
}