port PlayerActionEvent IsHittable

rename timing margin values
simplify some player action event logic
This commit is contained in:
minenice55 2023-06-25 18:53:36 -04:00
parent 6585753545
commit b9c5fd4581
7 changed files with 61 additions and 58 deletions

View file

@ -233,7 +233,7 @@ namespace HeavenStudio.Games.Scripts_CropStomp
stompedBeat = cond.songPositionInBeatsAsDouble; stompedBeat = cond.songPositionInBeatsAsDouble;
landBeat = targetBeat + (float)cond.SecsToBeats(Minigame.EndTime()-1, cond.GetBpmAtBeat(targetBeat)); landBeat = targetBeat + (float)cond.SecsToBeats(Minigame.NgLateTime()-1, cond.GetBpmAtBeat(targetBeat));
if (autoTriggered) if (autoTriggered)
{ {

View file

@ -201,7 +201,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
switch (forceHand) switch (forceHand)
{ {
case 0: case 0:
if (cond.songPositionInBeatsAsDouble - lastPunchTime < 0.25f + (Minigame.LateTime() - 1f)) if (cond.songPositionInBeatsAsDouble - lastPunchTime < 0.25f + (Minigame.JustLateTime() - 1f))
{ {
lastPunchTime = double.MinValue; lastPunchTime = double.MinValue;
anim.DoScaledAnimationAsync("Straight", 0.5f); anim.DoScaledAnimationAsync("Straight", 0.5f);

View file

@ -9,7 +9,7 @@ namespace HeavenStudio.Games
{ {
public class Minigame : MonoBehaviour public class Minigame : MonoBehaviour
{ {
public static double earlyTime = 0.075f, perfectTime = 0.06f, aceEarlyTime = 0.01f, aceLateTime = 0.01f, lateTime = 0.06f, endTime = 0.075f; public static double ngEarlyTime = 0.075f, justEarlyTime = 0.06f, aceEarlyTime = 0.01f, aceLateTime = 0.01f, justLateTime = 0.06f, ngLateTime = 0.075f;
public static float rankHiThreshold = 0.8f, rankOkThreshold = 0.6f; public static float rankHiThreshold = 0.8f, rankOkThreshold = 0.6f;
[SerializeField] public SoundSequence.SequenceKeyValue[] SoundSequences; [SerializeField] public SoundSequence.SequenceKeyValue[] SoundSequences;
@ -45,7 +45,9 @@ namespace HeavenStudio.Games
InputType inputType, InputType inputType,
PlayerActionEvent.ActionEventCallbackState OnHit, PlayerActionEvent.ActionEventCallbackState OnHit,
PlayerActionEvent.ActionEventCallback OnMiss, PlayerActionEvent.ActionEventCallback OnMiss,
PlayerActionEvent.ActionEventCallback OnBlank) PlayerActionEvent.ActionEventCallback OnBlank,
PlayerActionEvent.ActionEventHittableQuery HittableQuery = null
)
{ {
GameObject evtObj = new GameObject("ActionEvent" + (startBeat+timer)); GameObject evtObj = new GameObject("ActionEvent" + (startBeat+timer));
@ -59,6 +61,7 @@ namespace HeavenStudio.Games
evt.OnHit = OnHit; evt.OnHit = OnHit;
evt.OnMiss = OnMiss; evt.OnMiss = OnMiss;
evt.OnBlank = OnBlank; evt.OnBlank = OnBlank;
evt.IsHittable = HittableQuery;
evt.OnDestroy = RemoveScheduledInput; evt.OnDestroy = RemoveScheduledInput;
@ -98,8 +101,6 @@ namespace HeavenStudio.Games
return evt; return evt;
} }
//Clean up method used whenever a PlayerActionEvent has finished //Clean up method used whenever a PlayerActionEvent has finished
public void RemoveScheduledInput(PlayerActionEvent evt) public void RemoveScheduledInput(PlayerActionEvent evt)
{ {
@ -151,49 +152,36 @@ namespace HeavenStudio.Games
} }
// now should fix the fast bpm problem // now should fix the fast bpm problem
public static double EarlyTime() public static double NgEarlyTime()
{ {
return 1f - earlyTime; return 1f - ngEarlyTime;
} }
public static double PerfectTime() public static double JustEarlyTime()
{ {
return 1f - perfectTime; return 1f - justEarlyTime;
} }
public static double LateTime() public static double JustLateTime()
{ {
return 1f + lateTime; return 1f + justLateTime;
} }
public static double EndTime() public static double NgLateTime()
{ {
return 1f + endTime; return 1f + ngLateTime;
} }
public static double AceStartTime() public static double AceEarlyTime()
{ {
return 1f - aceEarlyTime; return 1f - aceEarlyTime;
} }
public static double AceEndTime() public static double AceLateTime()
{ {
return 1f + aceLateTime; return 1f + aceLateTime;
} }
// DEPRECATED: scales timing windows to the BPM in an ""intelligent"" manner
// only left for historical reasons
static float ScaleTimingMargin(float f)
{
float bpm = Conductor.instance.songBpm * Conductor.instance.musicSource.pitch;
float a = bpm / 120f;
float b = (Mathf.Log(a) + 2f) * 0.5f;
float r = Mathf.Lerp(a, b, 0.25f);
return r * f;
}
public int firstEnable = 0;
public virtual void OnGameSwitch(double beat) public virtual void OnGameSwitch(double beat)
{ {
//Below is a template that can be used for handling previous entities. //Below is a template that can be used for handling previous entities.
@ -263,7 +251,7 @@ namespace HeavenStudio.Games
public void ScoreMiss(double weight = 1f) public void ScoreMiss(double weight = 1f)
{ {
GameManager.instance.ScoreInputAccuracy(0, true, EndTime(), weight, false); GameManager.instance.ScoreInputAccuracy(0, true, NgLateTime(), weight, false);
if (weight > 0) if (weight > 0)
{ {
GoForAPerfect.instance.Miss(); GoForAPerfect.instance.Miss();

View file

@ -18,10 +18,12 @@ namespace HeavenStudio.Games
public static bool EnableAutoplayCheat = true; public static bool EnableAutoplayCheat = true;
public delegate void ActionEventCallback(PlayerActionEvent caller); public delegate void ActionEventCallback(PlayerActionEvent caller);
public delegate void ActionEventCallbackState(PlayerActionEvent caller, float state); public delegate void ActionEventCallbackState(PlayerActionEvent caller, float state);
public delegate bool ActionEventHittableQuery();
public ActionEventCallbackState OnHit; //Function to trigger when an input has been done perfectly public ActionEventCallbackState OnHit; //Function to trigger when an input has been done perfectly
public ActionEventCallback OnMiss; //Function to trigger when an input has been missed public ActionEventCallback OnMiss; //Function to trigger when an input has been missed
public ActionEventCallback OnBlank; //Function to trigger when an input has been recorded while this is pending public ActionEventCallback OnBlank; //Function to trigger when an input has been recorded while this is pending
public ActionEventHittableQuery IsHittable; //Checks if an input can be hit. Returning false will skip button checks.
public ActionEventCallback OnDestroy; //Function to trigger whenever this event gets destroyed. /!\ Shouldn't be used for a minigame! Use OnMiss instead /!\ public ActionEventCallback OnDestroy; //Function to trigger whenever this event gets destroyed. /!\ Shouldn't be used for a minigame! Use OnMiss instead /!\
@ -55,6 +57,11 @@ namespace HeavenStudio.Games
this.OnMiss = OnMiss; this.OnMiss = OnMiss;
} }
public void setHittableQuery(ActionEventHittableQuery IsHittable)
{
this.IsHittable = IsHittable;
}
public void Enable() { enabled = true; } public void Enable() { enabled = true; }
public void Disable() { enabled = false; } public void Disable() { enabled = false; }
public void QueueDeletion() { markForDeletion = true; } public void QueueDeletion() { markForDeletion = true; }
@ -105,7 +112,7 @@ namespace HeavenStudio.Games
} }
//BUGFIX: ActionEvents destroyed too early //BUGFIX: ActionEvents destroyed too early
if (normalizedTime > Minigame.EndTime()) Miss(); if (normalizedTime > Minigame.NgLateTime()) Miss();
if (lockedByEvent) if (lockedByEvent)
{ {
@ -116,11 +123,11 @@ namespace HeavenStudio.Games
return; return;
} }
if (!autoplayOnly && IsCorrectInput()) if (!autoplayOnly && (IsHittable == null || IsHittable != null && IsHittable()) && IsCorrectInput())
{ {
if (IsExpectingInputNow()) if (IsExpectingInputNow())
{ {
double stateProg = ((normalizedTime - Minigame.PerfectTime()) / (Minigame.LateTime() - Minigame.PerfectTime()) - 0.5f) * 2; double stateProg = ((normalizedTime - Minigame.JustEarlyTime()) / (Minigame.JustLateTime() - Minigame.JustEarlyTime()) - 0.5f) * 2;
Hit(stateProg, normalizedTime); Hit(stateProg, normalizedTime);
} }
else else
@ -186,8 +193,16 @@ namespace HeavenStudio.Games
public bool IsExpectingInputNow() public bool IsExpectingInputNow()
{ {
if (IsHittable != null)
{
if (!IsHittable()) return false;
}
if (!enabled) return false;
if (!isEligible) return false;
if (markForDeletion) return false;
double normalizedBeat = GetNormalizedTime(); double normalizedBeat = GetNormalizedTime();
return normalizedBeat > Minigame.EarlyTime() && normalizedBeat < Minigame.EndTime(); return normalizedBeat > Minigame.NgEarlyTime() && normalizedBeat < Minigame.NgLateTime();
} }
double GetNormalizedTime() double GetNormalizedTime()
@ -195,9 +210,9 @@ namespace HeavenStudio.Games
var cond = Conductor.instance; var cond = Conductor.instance;
double currTime = cond.songPositionAsDouble; double currTime = cond.songPositionAsDouble;
double targetTime = cond.GetSongPosFromBeat(startBeat + timer); double targetTime = cond.GetSongPosFromBeat(startBeat + timer);
double min = targetTime - 1f;
double max = targetTime + 1f; // HS timing window uses 1 as the middle point instead of 0
return 1f + (((currTime - min) / (max - min))-0.5f)*2; return 1 + (currTime - targetTime);
} }
//For the Autoplay //For the Autoplay
@ -210,7 +225,7 @@ namespace HeavenStudio.Games
else else
{ {
double normalizedBeat = GetNormalizedTime(); double normalizedBeat = GetNormalizedTime();
double stateProg = ((normalizedBeat - Minigame.PerfectTime()) / (Minigame.LateTime() - Minigame.PerfectTime()) - 0.5f) * 2; double stateProg = ((normalizedBeat - Minigame.JustEarlyTime()) / (Minigame.JustLateTime() - Minigame.JustEarlyTime()) - 0.5f) * 2;
Hit(stateProg, normalizedBeat); Hit(stateProg, normalizedBeat);
} }
} }
@ -251,27 +266,27 @@ namespace HeavenStudio.Games
double TimeToAccuracy(double time) double TimeToAccuracy(double time)
{ {
if (time >= Minigame.AceStartTime() && time <= Minigame.AceEndTime()) if (time >= Minigame.AceEarlyTime() && time <= Minigame.AceLateTime())
{ {
// Ace // Ace
return 1.0; return 1.0;
} }
double state = 0; double state = 0;
if (time >= Minigame.PerfectTime() && time <= Minigame.LateTime()) if (time >= Minigame.JustEarlyTime() && time <= Minigame.JustLateTime())
{ {
// Good Hit // Good Hit
if (time > 1.0) if (time > 1.0)
{ {
// late half of timing window // late half of timing window
state = 1.0 - ((time - Minigame.AceEndTime()) / (Minigame.LateTime() - Minigame.AceEndTime())); state = 1.0 - ((time - Minigame.AceLateTime()) / (Minigame.JustLateTime() - Minigame.AceLateTime()));
state *= 1.0 - Minigame.rankHiThreshold; state *= 1.0 - Minigame.rankHiThreshold;
state += Minigame.rankHiThreshold; state += Minigame.rankHiThreshold;
} }
else else
{ {
//early half of timing window //early half of timing window
state = ((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime())); state = ((time - Minigame.JustEarlyTime()) / (Minigame.AceEarlyTime() - Minigame.JustEarlyTime()));
state *= 1.0 - Minigame.rankHiThreshold; state *= 1.0 - Minigame.rankHiThreshold;
state += Minigame.rankHiThreshold; state += Minigame.rankHiThreshold;
} }
@ -281,13 +296,13 @@ namespace HeavenStudio.Games
if (time > 1.0) if (time > 1.0)
{ {
// late half of timing window // late half of timing window
state = 1.0 - ((time - Minigame.LateTime()) / (Minigame.EndTime() - Minigame.LateTime())); state = 1.0 - ((time - Minigame.JustLateTime()) / (Minigame.NgLateTime() - Minigame.JustLateTime()));
state *= Minigame.rankOkThreshold; state *= Minigame.rankOkThreshold;
} }
else else
{ {
//early half of timing window //early half of timing window
state = ((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime())); state = ((time - Minigame.JustEarlyTime()) / (Minigame.AceEarlyTime() - Minigame.JustEarlyTime()));
state *= Minigame.rankOkThreshold; state *= Minigame.rankOkThreshold;
} }
} }

View file

@ -343,7 +343,7 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
var cond = Conductor.instance; var cond = Conductor.instance;
ball = null; ball = null;
// queue the miss sound // queue the miss sound
MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound("spaceSoccer/missNeutral", targetBeat + (float)cond.SecsToBeats(Minigame.EndTime()-1, MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound("spaceSoccer/missNeutral", targetBeat + (float)cond.SecsToBeats(Minigame.NgLateTime()-1,
cond.GetBpmAtBeat(targetBeat)), SoundByte.GetPitchFromCents(UnityEngine.Random.Range(-75, 75), false)) }); cond.GetBpmAtBeat(targetBeat)), SoundByte.GetPitchFromCents(UnityEngine.Random.Range(-75, 75), false)) });
} }

View file

@ -41,13 +41,13 @@ namespace HeavenStudio.Common
{ {
if (cond.songPositionInBeatsAsDouble > starStart && state == StarState.In) if (cond.songPositionInBeatsAsDouble > starStart && state == StarState.In)
{ {
double offset = cond.SecsToBeats(Minigame.AceStartTime()-1, cond.GetBpmAtBeat(StarTargetTime)); double offset = cond.SecsToBeats(Minigame.AceEarlyTime()-1, cond.GetBpmAtBeat(StarTargetTime));
if (cond.songPositionInBeatsAsDouble <= starStart + starLength + offset) if (cond.songPositionInBeatsAsDouble <= starStart + starLength + offset)
starAnim.DoScaledAnimation("StarIn", starStart, starLength + (float)offset); starAnim.DoScaledAnimation("StarIn", starStart, starLength + (float)offset);
else else
starAnim.Play("StarIn", -1, 1f); starAnim.Play("StarIn", -1, 1f);
offset = cond.SecsToBeats(Minigame.AceEndTime()-1, cond.GetBpmAtBeat(StarTargetTime)); offset = cond.SecsToBeats(Minigame.AceLateTime()-1, cond.GetBpmAtBeat(StarTargetTime));
if (cond.songPositionInBeatsAsDouble > starStart + starLength + offset) if (cond.songPositionInBeatsAsDouble > starStart + starLength + offset)
KillStar(); KillStar();
} }
@ -94,8 +94,8 @@ namespace HeavenStudio.Common
public bool DoStarJust() public bool DoStarJust()
{ {
if (state == StarState.In && if (state == StarState.In &&
cond.songPositionInBeatsAsDouble >= StarTargetTime + cond.SecsToBeats(Minigame.AceStartTime()-1, cond.GetBpmAtBeat(StarTargetTime)) && cond.songPositionInBeatsAsDouble >= StarTargetTime + cond.SecsToBeats(Minigame.AceEarlyTime()-1, cond.GetBpmAtBeat(StarTargetTime)) &&
cond.songPositionInBeatsAsDouble <= StarTargetTime + cond.SecsToBeats(Minigame.AceEndTime()-1, cond.GetBpmAtBeat(StarTargetTime)) cond.songPositionInBeatsAsDouble <= StarTargetTime + cond.SecsToBeats(Minigame.AceLateTime()-1, cond.GetBpmAtBeat(StarTargetTime))
) )
{ {
state = StarState.Collected; state = StarState.Collected;

View file

@ -87,12 +87,12 @@ namespace HeavenStudio.Common
// SetArrowPos(time); // SetArrowPos(time);
// no Clamp() because double // no Clamp() because double
time = System.Math.Max(Minigame.EarlyTime(), System.Math.Min(Minigame.EndTime(), time)); time = System.Math.Max(Minigame.NgEarlyTime(), System.Math.Min(Minigame.NgLateTime(), time));
if (time >= Minigame.AceStartTime() && time <= Minigame.AceEndTime()) if (time >= Minigame.AceEarlyTime() && time <= Minigame.AceLateTime())
{ {
type = Rating.Just; type = Rating.Just;
frac = (float)((time - Minigame.AceStartTime()) / (Minigame.AceEndTime() - Minigame.AceStartTime())); frac = (float)((time - Minigame.AceEarlyTime()) / (Minigame.AceLateTime() - Minigame.AceEarlyTime()));
y = barJustTransform.localScale.y * frac - (barJustTransform.localScale.y * 0.5f); y = barJustTransform.localScale.y * frac - (barJustTransform.localScale.y * 0.5f);
} }
else else
@ -100,32 +100,32 @@ namespace HeavenStudio.Common
if (time > 1.0) if (time > 1.0)
{ {
// goes "down" // goes "down"
if (time <= Minigame.LateTime()) if (time <= Minigame.JustLateTime())
{ {
type = Rating.OK; type = Rating.OK;
frac = (float)((time - Minigame.AceEndTime()) / (Minigame.LateTime() - Minigame.AceEndTime())); frac = (float)((time - Minigame.AceLateTime()) / (Minigame.JustLateTime() - Minigame.AceLateTime()));
y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * frac) + barJustTransform.localScale.y; y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * frac) + barJustTransform.localScale.y;
} }
else else
{ {
type = Rating.NG; type = Rating.NG;
frac = (float)((time - Minigame.LateTime()) / (Minigame.EndTime() - Minigame.LateTime())); frac = (float)((time - Minigame.JustLateTime()) / (Minigame.NgLateTime() - Minigame.JustLateTime()));
y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * frac) + barOKTransform.localScale.y; y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * frac) + barOKTransform.localScale.y;
} }
} }
else else
{ {
// goes "up" // goes "up"
if (time >= Minigame.PerfectTime()) if (time >= Minigame.JustEarlyTime())
{ {
type = Rating.OK; type = Rating.OK;
frac = (float)((time - Minigame.PerfectTime()) / (Minigame.AceStartTime() - Minigame.PerfectTime())); frac = (float)((time - Minigame.JustEarlyTime()) / (Minigame.AceEarlyTime() - Minigame.JustEarlyTime()));
y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * -frac) - barJustTransform.localScale.y; y = ((barOKTransform.localScale.y - barJustTransform.localScale.y) * -frac) - barJustTransform.localScale.y;
} }
else else
{ {
type = Rating.NG; type = Rating.NG;
frac = (float)((time - Minigame.EarlyTime()) / (Minigame.PerfectTime() - Minigame.EarlyTime())); frac = (float)((time - Minigame.NgEarlyTime()) / (Minigame.JustEarlyTime() - Minigame.NgEarlyTime()));
y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * -frac) - barOKTransform.localScale.y; y = ((barNGTransform.localScale.y - barOKTransform.localScale.y) * -frac) - barOKTransform.localScale.y;
} }
} }