2022-02-03 02:09:50 +00:00
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
2022-02-14 08:53:58 +00:00
using NaughtyBezierCurves ;
2022-02-03 02:09:50 +00:00
2022-03-14 14:21:05 +00:00
using HeavenStudio.Util ;
2023-10-29 19:44:47 +00:00
using HeavenStudio.InputSystem ;
2022-04-12 16:14:46 +00:00
2024-01-05 02:07:27 +00:00
using Jukebox ;
2022-04-12 16:14:46 +00:00
namespace HeavenStudio.Games.Loaders
{
using static Minigames ;
public static class NtrPingpongLoader
{
2023-10-29 19:44:47 +00:00
public static Minigame AddGame ( EventCaller eventCaller )
{
2023-05-21 17:35:00 +00:00
return new Minigame ( "rhythmRally" , "Rhythm Rally" , "ffffff" , false , false , new List < GameAction > ( )
2022-04-12 16:14:46 +00:00
{
2022-08-21 03:13:52 +00:00
new GameAction ( "bop" , "Bop" )
{
2023-03-07 17:22:32 +00:00
function = delegate { var e = eventCaller . currentEntity ; RhythmRally . instance . Bop ( e . beat , e . length , e [ "bop" ] , e [ "bopAuto" ] ) ; } ,
resizable = true ,
parameters = new List < Param > ( )
{
2024-01-15 02:04:10 +00:00
new Param ( "bop" , true , "Bop" , "Toggle if the paddlers should bop for the duration of this event." ) ,
new Param ( "bopAuto" , false , "Bop (Auto)" , "Toggle if the paddlers should automatically bop until another Bop event is reached." )
2023-03-07 17:22:32 +00:00
}
2022-08-21 03:13:52 +00:00
} ,
2022-08-23 13:09:43 +00:00
new GameAction ( "whistle" , "Whistle" )
2022-08-21 03:13:52 +00:00
{
2023-02-14 16:31:51 +00:00
preFunction = delegate { RhythmRally . PlayWhistle ( eventCaller . currentEntity . beat ) ; } ,
2022-08-21 03:13:52 +00:00
defaultLength = 0.5f
} ,
new GameAction ( "toss ball" , "Toss Ball" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . Toss ( eventCaller . currentEntity . beat , eventCaller . currentEntity . length , 6f , true ) ; } ,
defaultLength = 2f ,
2023-01-16 03:05:25 +00:00
resizable = true
2022-08-21 03:13:52 +00:00
} ,
new GameAction ( "rally" , "Rally" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . Serve ( eventCaller . currentEntity . beat , RhythmRally . RallySpeed . Normal ) ; } ,
defaultLength = 4f ,
2022-08-21 03:13:52 +00:00
resizable = true
} ,
new GameAction ( "slow rally" , "Slow Rally" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . Serve ( eventCaller . currentEntity . beat , RhythmRally . RallySpeed . Slow ) ; } ,
defaultLength = 8f ,
2022-08-21 03:13:52 +00:00
resizable = true
} ,
new GameAction ( "fast rally" , "Fast Rally" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . PrepareFastRally ( eventCaller . currentEntity . beat , RhythmRally . RallySpeed . Fast , eventCaller . currentEntity [ "muteAudio" ] ) ; } ,
2023-02-14 16:31:51 +00:00
defaultLength = 6f ,
parameters = new List < Param > ( )
{
2024-01-15 02:04:10 +00:00
new Param ( "muteAudio" , false , "Mute Cowbell" , "Toggle if the cowbell \"tonk-tink-tonk\" sound should be muted." )
2023-02-14 16:31:51 +00:00
}
2022-08-21 03:13:52 +00:00
} ,
new GameAction ( "superfast rally" , "Superfast Rally" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . PrepareFastRally ( eventCaller . currentEntity . beat , RhythmRally . RallySpeed . SuperFast , eventCaller . currentEntity [ "muteAudio" ] ) ; } ,
2023-02-14 16:31:51 +00:00
defaultLength = 12f ,
parameters = new List < Param > ( )
{
2024-01-15 02:04:10 +00:00
new Param ( "muteAudio" , false , "Mute Cowbell" , "Toggle if the cowbell \"tonk-tink-tonk\" sound should be muted." )
2023-02-14 16:31:51 +00:00
}
2022-08-21 03:13:52 +00:00
} ,
2023-03-07 17:22:32 +00:00
new GameAction ( "tonktinktonk" , "Tonk-Tink-Tonk (Stretchable)" )
{
preFunction = delegate { var e = eventCaller . currentEntity ; RhythmRally . TonkTinkTonkStretchable ( e . beat , e . length ) ; } ,
defaultLength = 4f ,
resizable = true
} ,
new GameAction ( "superfast stretchable" , "Superfast Rally (Stretchable)" )
{
function = delegate { var e = eventCaller . currentEntity ; RhythmRally . instance . SuperFastRallyStretchable ( e . beat , e . length ) ; } ,
defaultLength = 8f ,
resizable = true
} ,
2022-08-21 03:13:52 +00:00
new GameAction ( "pose" , "End Pose" )
{
2023-03-07 17:22:32 +00:00
function = delegate { RhythmRally . instance . Pose ( ) ; } ,
2022-08-21 03:13:52 +00:00
defaultLength = 0.5f
} ,
new GameAction ( "camera" , "Camera Controls" )
{
function = delegate {
var e = eventCaller . currentEntity ;
2024-01-05 02:07:27 +00:00
// var rotation = new Vector3(0, e["valA"], 0);
2024-01-06 03:51:27 +00:00
RhythmRally . instance . ChangeCameraAngle ( e . beat , e [ "valA" ] , e [ "valB" ] , e . length , ( Util . EasingFunction . Ease ) e [ "type" ] , e [ "additive" ] ) ;
2023-03-07 17:22:32 +00:00
} ,
defaultLength = 4 ,
resizable = true ,
2022-08-21 03:13:52 +00:00
parameters = new List < Param > ( ) {
2024-01-15 02:04:10 +00:00
new Param ( "valA" , new EntityTypes . Integer ( - 360 , 360 , 0 ) , "Rotation" , "Set the rotation of the camera around the center of the table." ) ,
new Param ( "valB" , new EntityTypes . Float ( 0.5f , 4f , 1 ) , "Zoom" , "Set the camera's level of zoom." ) ,
new Param ( "type" , Util . EasingFunction . Ease . Linear , "Ease" , "Set the easing of the action." ) ,
new Param ( "additive" , true , "Additive Rotation" , "Toggle if the above rotation should be added to the current angle instead of setting the target angle to travel to." )
2023-03-07 17:22:32 +00:00
}
2022-08-21 03:13:52 +00:00
} ,
2024-01-05 02:07:27 +00:00
// todo: background recolouring
// new GameAction("bg colour", "Background Colour")
// {
// function = delegate {
// var e = eventCaller.currentEntity;
// },
// defaultLength = 1,
// resizable = true,
// parameters = new List<Param>() {
// new Param("bottomColour", new Color(0,0,0,0), "Bottom Colour", "The colour at the bottom of the skybox"),
// new Param("topColour", new Color(1,1,1,1), "Top Colour", "The colour at the top of the skybox"),
// new Param("type", Util.EasingFunction.Ease.Linear, "Ease", "The easing function to use"),
// }
// },
2023-05-28 17:34:44 +00:00
} ,
2023-10-29 19:44:47 +00:00
new List < string > ( ) { "ntr" , "keep" } ,
2023-05-28 17:34:44 +00:00
"ntrpingpong" , "en" ,
2023-10-29 19:44:47 +00:00
new List < string > ( ) { }
2023-05-28 17:34:44 +00:00
) ;
2022-04-12 16:14:46 +00:00
}
}
}
2022-03-14 14:21:05 +00:00
namespace HeavenStudio.Games
2022-02-03 02:09:50 +00:00
{
2022-03-12 04:10:13 +00:00
using Scripts_RhythmRally ;
2022-02-12 08:12:12 +00:00
public class RhythmRally : Minigame
2022-02-03 02:09:50 +00:00
{
2022-02-14 08:53:58 +00:00
public enum RallySpeed { Slow , Normal , Fast , SuperFast }
2022-02-12 16:57:45 +00:00
2022-02-14 08:53:58 +00:00
[Header("Camera")]
2023-04-23 19:27:23 +00:00
[SerializeField] Transform cameraPivot ;
[SerializeField] Transform cameraPos ;
[SerializeField] float cameraFOV ;
2022-02-14 08:53:58 +00:00
[Header("Ball and curve info")]
2022-02-04 18:15:28 +00:00
public GameObject ball ;
2022-02-14 08:53:58 +00:00
public GameObject ballShadow ;
2024-01-05 02:07:27 +00:00
public ParticleSystem ballTrail ;
2022-02-14 08:53:58 +00:00
public BezierCurve3D serveCurve ;
public BezierCurve3D returnCurve ;
2022-02-22 06:43:24 +00:00
public BezierCurve3D tossCurve ;
2022-03-05 19:30:05 +00:00
public BezierCurve3D missCurve ;
2022-02-15 05:17:53 +00:00
public GameObject ballHitFX ;
2022-02-14 08:53:58 +00:00
2022-02-04 18:15:28 +00:00
2022-02-14 08:53:58 +00:00
[Header("Animators")]
2022-02-12 08:12:12 +00:00
public Animator playerAnim ;
public Animator opponentAnim ;
2022-02-14 08:53:58 +00:00
[Header("Properties")]
public RallySpeed rallySpeed = RallySpeed . Normal ;
public bool started ;
public bool missed ;
public bool served ;
2022-02-22 06:43:24 +00:00
public bool tossing ;
2023-06-10 19:13:29 +00:00
public double serveBeat ;
public double targetBeat ;
public double tossBeat ;
public double missBeat ;
2022-02-22 06:43:24 +00:00
public float tossLength ;
2022-02-15 05:17:53 +00:00
private bool inPose ;
2022-02-14 08:53:58 +00:00
public Paddlers paddlers ;
2022-02-12 08:12:12 +00:00
public static RhythmRally instance ;
private void Awake ( )
{
2023-11-21 16:57:03 +00:00
GameCamera . AdditionalPosition = cameraPos . position + ( Quaternion . Euler ( cameraPos . rotation . eulerAngles ) * Vector3 . forward * 10f ) ;
GameCamera . AdditionalRotEuler = cameraPos . rotation . eulerAngles ;
GameCamera . AdditionalFoV = cameraFOV ;
2022-02-12 08:12:12 +00:00
instance = this ;
2022-03-26 02:08:46 +00:00
paddlers . Init ( ) ;
2022-02-12 08:12:12 +00:00
playerAnim . Play ( "Idle" , 0 , 0 ) ;
opponentAnim . Play ( "Idle" , 0 , 0 ) ;
2023-12-05 22:38:52 +00:00
SetupBopRegion ( "rhythmRally" , "bop" , "bopAuto" ) ;
2022-02-03 02:09:50 +00:00
}
2024-01-05 02:07:27 +00:00
private void Start ( )
{
EntityPreCheck ( Conductor . instance . songPositionInBeatsAsDouble ) ;
}
2022-02-14 08:53:58 +00:00
const float tableHitTime = 0.58f ;
bool opponentServing = false ; // Opponent serving this frame?
2024-01-05 02:07:27 +00:00
double cameraRotateBeat = double . MaxValue ;
double cameraRotateLength ;
Util . EasingFunction . Ease cameraRotateEase ;
float cameraRotateLast = 0 , cameraScaleLast = 1 ;
float cameraRotateNext = 0 , cameraScaleNext = 1 ;
2022-02-03 02:09:50 +00:00
void Update ( )
{
2022-02-12 08:12:12 +00:00
var cond = Conductor . instance ;
2023-06-10 19:13:29 +00:00
var currentBeat = cond . songPositionInBeatsAsDouble ;
2023-10-29 19:44:47 +00:00
2022-02-14 08:53:58 +00:00
var hitBeat = serveBeat ; // Beat when the last paddler hit the ball
var beatDur1 = 1f ; // From paddle to table
var beatDur2 = 1f ; // From table to other paddle
var playerState = playerAnim . GetCurrentAnimatorStateInfo ( 0 ) ;
var opponentState = opponentAnim . GetCurrentAnimatorStateInfo ( 0 ) ;
if ( started )
{
// Determine hitBeat and beatDurs.
switch ( rallySpeed )
{
case RallySpeed . Normal :
if ( ! served )
{
hitBeat = serveBeat + 2f ;
}
break ;
case RallySpeed . Fast :
if ( ! served )
{
hitBeat = serveBeat + 1f ;
beatDur1 = 1f ;
beatDur2 = 2f ;
}
else
{
beatDur1 = 0.5f ;
beatDur2 = 0.5f ;
}
break ;
case RallySpeed . SuperFast :
if ( ! served )
{
hitBeat = serveBeat + 1f ;
}
beatDur1 = 0.5f ;
beatDur2 = 0.5f ;
break ;
case RallySpeed . Slow :
if ( ! served )
{
hitBeat = serveBeat + 4f ;
}
beatDur1 = 2f ;
beatDur2 = 2f ;
break ;
}
// Ball position.
var curveToUse = served ? serveCurve : returnCurve ;
float curvePosition ;
var hitPosition1 = cond . GetPositionFromBeat ( hitBeat , beatDur1 ) ;
if ( hitPosition1 > = 1f )
{
var hitPosition2 = cond . GetPositionFromBeat ( hitBeat + beatDur1 , beatDur2 ) ;
curvePosition = tableHitTime + hitPosition2 * ( 1f - tableHitTime ) ;
}
else
{
curvePosition = hitPosition1 * tableHitTime ;
}
if ( ! missed )
{
2022-03-05 19:30:05 +00:00
float curveHeight = 1.25f ;
if ( ( rallySpeed = = RallySpeed . Fast & & served ) | | rallySpeed = = RallySpeed . SuperFast )
curveHeight = 0.75f ;
2022-02-15 05:17:53 +00:00
else if ( rallySpeed = = RallySpeed . Fast & & ! served & & hitPosition1 > = 1f )
2022-02-14 08:53:58 +00:00
curveHeight = 2f ;
else if ( rallySpeed = = RallySpeed . Slow )
curveHeight = 3f ;
curveToUse . transform . localScale = new Vector3 ( 1f , curveHeight , 1f ) ;
2022-03-05 17:41:54 +00:00
ball . transform . position = curveToUse . GetPoint ( Mathf . Max ( 0 , curvePosition ) ) ;
2022-03-05 19:30:05 +00:00
// Make ball inactive before it passes through the floor.
if ( curvePosition > 1.05f )
ball . SetActive ( false ) ;
2022-02-14 08:53:58 +00:00
}
2022-02-22 06:43:24 +00:00
else
{
if ( tossing )
{
TossUpdate ( tossBeat , tossLength ) ;
}
2022-03-05 19:30:05 +00:00
else
{
var missPosition = cond . GetPositionFromBeat ( missBeat , 1f ) ;
ball . transform . position = missCurve . GetPoint ( Mathf . Max ( 0 , missPosition ) ) ;
if ( missPosition > 1f )
ball . SetActive ( false ) ;
}
2022-02-22 06:43:24 +00:00
}
2022-02-14 08:53:58 +00:00
ballShadow . transform . position = new Vector3 ( ball . transform . position . x , - 0.399f , ball . transform . position . z ) ;
var timeBeforeNextHit = hitBeat + beatDur1 + beatDur2 - currentBeat ;
// Check if the opponent should swing.
if ( ! served & & timeBeforeNextHit < = 0f )
{
2023-06-10 19:13:29 +00:00
var rallies = GameManager . instance . Beatmap . Entities . FindAll ( c = > c . datamodel = = "rhythmRally/rally" | | c . datamodel = = "rhythmRally/slow rally" ) ;
2022-02-14 08:53:58 +00:00
for ( int i = 0 ; i < rallies . Count ; i + + )
{
var rally = rallies [ i ] ;
if ( rally . beat - currentBeat < = 0f & & rally . beat + rally . length - currentBeat > 0f )
{
Serve ( hitBeat + beatDur1 + beatDur2 , rallySpeed ) ;
opponentServing = true ;
break ;
}
}
}
// Check if paddler should do ready animation.
bool readyToPrep ;
2022-03-05 19:30:05 +00:00
switch ( rallySpeed )
{
case RallySpeed . Slow :
case RallySpeed . Fast :
readyToPrep = timeBeforeNextHit < = 2f ;
break ;
case RallySpeed . SuperFast :
readyToPrep = timeBeforeNextHit < = 0.5f ;
break ;
default :
readyToPrep = timeBeforeNextHit < = 1f ;
break ;
}
2022-02-14 08:53:58 +00:00
// Paddler ready animation.
2022-02-15 05:17:53 +00:00
if ( readyToPrep & & ! opponentServing & & ! inPose )
2022-02-14 08:53:58 +00:00
{
if ( served )
{
2023-10-29 19:44:47 +00:00
if ( PlayerInput . CurrentControlStyle ! = InputController . ControlStyles . Touch | | GameManager . instance . autoplay )
{
if ( ( playerState . IsName ( "Swing" ) & & playerAnim . IsAnimationNotPlaying ( ) ) | | ( ! playerState . IsName ( "Swing" ) & & ! playerState . IsName ( "Ready1" ) ) )
playerAnim . Play ( "Ready1" ) ;
}
2022-02-14 08:53:58 +00:00
}
else if ( ! opponentServing )
{
if ( ( opponentState . IsName ( "Swing" ) & & opponentAnim . IsAnimationNotPlaying ( ) ) | | ( ! opponentState . IsName ( "Swing" ) & & ! opponentState . IsName ( "Ready1" ) ) )
2022-02-22 06:43:24 +00:00
{
2022-02-14 08:53:58 +00:00
opponentAnim . Play ( "Ready1" ) ;
2022-02-22 06:43:24 +00:00
// Toss ball if it fell off the table.
if ( missed & & ! tossing )
{
float tossHeight = 3f ;
if ( rallySpeed = = RallySpeed . Slow | | rallySpeed = = RallySpeed . Fast )
tossHeight = 6f ;
Toss ( hitBeat + beatDur1 , beatDur2 , tossHeight ) ;
}
}
2023-10-29 19:44:47 +00:00
2022-02-14 08:53:58 +00:00
// If player never swung and is still in ready state, snap them out of it.
2024-01-05 02:07:27 +00:00
// only if they're not manually preparing via touch controls
if ( missed & & playerState . IsName ( "Ready1" ) & & ( ! paddlers . PlayerDown ) )
2022-02-14 08:53:58 +00:00
playerAnim . Play ( "Beat" ) ;
}
}
}
2022-02-22 06:43:24 +00:00
else
{
if ( tossing )
{
TossUpdate ( tossBeat , tossLength ) ;
}
}
2022-02-12 08:12:12 +00:00
2022-02-14 08:53:58 +00:00
opponentServing = false ;
2023-04-23 19:27:23 +00:00
//update camera
2024-01-05 02:07:27 +00:00
UpdateCamera ( currentBeat ) ;
}
public override void OnPlay ( double beat )
{
EntityPreCheck ( beat ) ;
}
void EntityPreCheck ( double beat )
{
cameraRotateBeat = double . MaxValue ;
cameraRotateLength = 0 ;
cameraRotateEase = Util . EasingFunction . Ease . Linear ;
cameraRotateLast = 0 ; cameraScaleLast = 1 ;
cameraRotateNext = 0 ; cameraScaleNext = 1 ;
List < RiqEntity > prevEntities = GameManager . instance . Beatmap . Entities . FindAll ( c = > c . beat < beat & & c . datamodel . Split ( 0 ) = = "rhythmRally" ) ;
RiqEntity lastGameSwitch = GameManager . instance . Beatmap . Entities . FindLast ( c = > c . beat < = beat & & c . datamodel = = "gameManager/switchGame/rhythmRally" ) ;
if ( lastGameSwitch = = null ) return ;
List < RiqEntity > cameraEntities = prevEntities . FindAll ( c = > c . beat > = lastGameSwitch . beat & & c . datamodel = = "rhythmRally/camera" ) ;
foreach ( var entity in cameraEntities )
{
2024-01-06 03:51:27 +00:00
ChangeCameraAngle ( entity . beat , entity [ "valA" ] , entity [ "valB" ] , entity . length , ( Util . EasingFunction . Ease ) entity [ "type" ] , entity [ "additive" ] ) ;
2024-01-05 02:07:27 +00:00
}
UpdateCamera ( beat ) ;
}
void UpdateCamera ( double beat )
{
if ( beat > = cameraRotateBeat )
{
Util . EasingFunction . Function func = Util . EasingFunction . GetEasingFunction ( cameraRotateEase ) ;
float rotProg = Conductor . instance . GetPositionFromBeat ( cameraRotateBeat , cameraRotateLength , true ) ;
2024-01-06 03:51:27 +00:00
rotProg = Mathf . Clamp01 ( rotProg ) ;
2024-01-05 02:07:27 +00:00
float rot = func ( cameraRotateLast , cameraRotateNext , rotProg ) ;
cameraPivot . rotation = Quaternion . Euler ( 0 , rot , 0 ) ;
cameraPivot . localScale = Vector3 . one * func ( cameraScaleLast , cameraScaleNext , rotProg ) ;
}
2023-11-21 16:57:03 +00:00
GameCamera . AdditionalPosition = cameraPos . position + ( Quaternion . Euler ( cameraPos . rotation . eulerAngles ) * Vector3 . forward * 10f ) ;
GameCamera . AdditionalRotEuler = cameraPos . rotation . eulerAngles ;
GameCamera . AdditionalFoV = cameraFOV ;
2022-02-12 08:12:12 +00:00
}
2023-11-23 16:19:39 +00:00
public override void OnBeatPulse ( double beat )
{
2023-12-05 22:38:52 +00:00
if ( BeatIsInBopRegion ( beat ) & & ! inPose )
2023-11-23 16:19:39 +00:00
{
BopSingle ( ) ;
}
}
2023-06-10 19:13:29 +00:00
public void Bop ( double beat , float length , bool bop , bool bopAuto )
2022-02-12 08:12:12 +00:00
{
2023-03-07 17:22:32 +00:00
if ( bop )
{
for ( int i = 0 ; i < length ; i + + )
{
2023-09-11 22:28:04 +00:00
BeatAction . New ( instance , new List < BeatAction . Action > ( )
2023-03-07 17:22:32 +00:00
{
new BeatAction . Action ( beat + i , delegate
{
BopSingle ( ) ;
} )
} ) ;
}
}
}
void BopSingle ( )
{
var playerState = playerAnim . GetCurrentAnimatorStateInfo ( 0 ) ;
var opponentState = opponentAnim . GetCurrentAnimatorStateInfo ( 0 ) ;
bool playerPrepping = false ; // Player using prep animation?
bool opponentPrepping = false ; // Opponent using prep animation?
2024-01-05 02:07:27 +00:00
if ( ( ! playerPrepping ) & & ( ! paddlers . PlayerDown ) & & ( playerAnim . IsAnimationNotPlaying ( ) | | playerState . IsName ( "Idle" ) | | playerState . IsName ( "Beat" ) ) )
2023-03-07 17:22:32 +00:00
playerAnim . DoScaledAnimationAsync ( "Beat" , 0.5f ) ;
2024-01-05 02:07:27 +00:00
if ( ( ! opponentPrepping ) & & ( ! opponentServing ) & & ( ! tossing ) & & ( opponentAnim . IsAnimationNotPlaying ( ) | | opponentState . IsName ( "Idle" ) | | opponentState . IsName ( "Beat" ) ) )
2023-03-07 17:22:32 +00:00
opponentAnim . DoScaledAnimationAsync ( "Beat" , 0.5f ) ;
2022-02-03 02:09:50 +00:00
}
2022-02-14 08:53:58 +00:00
2023-06-10 19:13:29 +00:00
public void Serve ( double beat , RallySpeed speed )
2022-02-14 08:53:58 +00:00
{
2022-03-05 17:41:54 +00:00
if ( ! ball . activeSelf )
ball . SetActive ( true ) ;
2022-03-05 19:30:05 +00:00
if ( ! ballTrail . gameObject . activeSelf )
ballTrail . gameObject . SetActive ( true ) ;
2022-02-14 08:53:58 +00:00
served = true ;
missed = false ;
started = true ;
opponentServing = true ;
2022-02-22 06:43:24 +00:00
tossing = false ;
2023-10-29 19:44:47 +00:00
2022-02-14 08:53:58 +00:00
serveBeat = beat ;
rallySpeed = speed ;
2023-06-10 19:13:29 +00:00
double bounceBeat = 0f ;
2022-02-14 08:53:58 +00:00
switch ( rallySpeed )
{
case RallySpeed . Normal :
2023-01-16 03:05:25 +00:00
targetBeat = 2f ;
2022-02-14 08:53:58 +00:00
bounceBeat = serveBeat + 1f ;
break ;
case RallySpeed . Fast :
case RallySpeed . SuperFast :
2023-01-16 03:05:25 +00:00
targetBeat = 1f ;
2022-02-14 08:53:58 +00:00
bounceBeat = serveBeat + 0.5f ;
break ;
case RallySpeed . Slow :
2023-01-16 03:05:25 +00:00
targetBeat = 4f ;
2022-02-14 08:53:58 +00:00
bounceBeat = serveBeat + 2f ;
break ;
}
2023-02-17 18:45:18 +00:00
opponentAnim . DoScaledAnimationAsync ( "Swing" , 0.5f ) ;
2022-02-14 08:53:58 +00:00
MultiSound . Play ( new MultiSound . Sound [ ] { new MultiSound . Sound ( "rhythmRally/Serve" , serveBeat ) , new MultiSound . Sound ( "rhythmRally/ServeBounce" , bounceBeat ) } ) ;
2022-02-15 05:17:53 +00:00
paddlers . BounceFX ( bounceBeat ) ;
2022-02-14 08:53:58 +00:00
2023-10-29 19:44:47 +00:00
ScheduleInput ( serveBeat , targetBeat , InputAction_FlickPress , paddlers . Just , paddlers . Miss , paddlers . Out ) ;
2022-02-14 08:53:58 +00:00
}
2023-06-10 19:13:29 +00:00
public void Toss ( double beat , float length , float height , bool firstToss = false )
2022-02-22 06:43:24 +00:00
{
2022-03-05 19:30:05 +00:00
// Hide trail while tossing to prevent weirdness while teleporting ball.
ballTrail . gameObject . SetActive ( false ) ;
2023-01-16 03:05:25 +00:00
if ( firstToss )
2023-10-29 19:44:47 +00:00
height * = length / 2f ;
2023-01-16 03:05:25 +00:00
2022-02-22 06:43:24 +00:00
tossCurve . transform . localScale = new Vector3 ( 1f , height , 1f ) ;
tossBeat = beat ;
tossLength = length ;
tossing = true ;
if ( firstToss )
{
opponentAnim . Play ( "Ready1" ) ;
}
2022-03-05 17:41:54 +00:00
if ( ! ball . activeSelf )
ball . SetActive ( true ) ;
2022-02-22 06:43:24 +00:00
}
2023-06-10 19:13:29 +00:00
private void TossUpdate ( double beat , float duration )
2022-02-22 06:43:24 +00:00
{
var tossPosition = Conductor . instance . GetPositionFromBeat ( beat , duration ) ;
ball . transform . position = tossCurve . GetPoint ( Mathf . Clamp ( tossPosition , 0 , 1 ) ) ;
2022-03-05 19:30:05 +00:00
if ( tossPosition > 1.05f )
ball . SetActive ( false ) ;
2022-02-22 06:43:24 +00:00
}
2023-06-10 19:13:29 +00:00
public static void PlayWhistle ( double beat )
2022-02-23 03:34:11 +00:00
{
2023-02-14 16:31:51 +00:00
MultiSound . Play ( new MultiSound . Sound [ ]
{
new MultiSound . Sound ( "rhythmRally/Whistle" , beat ) ,
} , forcePlay : true ) ;
2022-02-23 03:34:11 +00:00
}
2022-02-15 05:17:53 +00:00
public void Pose ( )
{
playerAnim . Play ( "Pose" , 0 , 0 ) ;
opponentAnim . Play ( "Pose" , 0 , 0 ) ;
2022-03-05 17:41:54 +00:00
ball . SetActive ( false ) ; // temporary solution, should realistically just fall down
2022-02-15 05:17:53 +00:00
inPose = true ;
}
2024-01-06 03:51:27 +00:00
public void ChangeCameraAngle ( double beat , float rotation , float camZoom , double length , Util . EasingFunction . Ease ease , bool additive = true )
2022-02-28 19:31:28 +00:00
{
2024-01-05 02:07:27 +00:00
cameraRotateBeat = beat ;
cameraRotateLength = length ;
cameraRotateEase = ease ;
2024-01-06 03:51:27 +00:00
cameraRotateLast = cameraRotateNext % 360f ;
2024-01-05 02:07:27 +00:00
cameraScaleLast = cameraScaleNext ;
cameraScaleNext = camZoom ;
2024-01-06 03:51:27 +00:00
if ( additive )
{
cameraRotateNext = cameraRotateLast + rotation ;
}
else
{
cameraRotateNext = rotation ;
}
2022-02-28 19:31:28 +00:00
}
2023-06-10 19:13:29 +00:00
public void PrepareFastRally ( double beat , RallySpeed speedChange , bool muteAudio = false )
2022-02-14 08:53:58 +00:00
{
if ( speedChange = = RallySpeed . Fast )
{
2023-09-11 22:28:04 +00:00
BeatAction . New ( this , new List < BeatAction . Action > ( )
2022-02-14 08:53:58 +00:00
{
new BeatAction . Action ( beat + 2f , delegate { Serve ( beat + 2f , RallySpeed . Fast ) ; } )
} ) ;
2023-02-14 16:31:51 +00:00
if ( muteAudio ) return ;
2023-03-07 17:22:32 +00:00
TonkTinkTonkStretchable ( beat , 1.5f ) ;
2022-02-14 08:53:58 +00:00
}
else if ( speedChange = = RallySpeed . SuperFast )
{
2023-03-07 17:22:32 +00:00
SuperFastRallyStretchable ( beat + 4f , 8f ) ;
2022-02-14 08:53:58 +00:00
2023-02-14 16:31:51 +00:00
if ( muteAudio ) return ;
2023-03-07 17:22:32 +00:00
TonkTinkTonkStretchable ( beat , 4f ) ;
}
}
2023-06-10 19:13:29 +00:00
public static void TonkTinkTonkStretchable ( double beat , float length )
2023-03-07 17:22:32 +00:00
{
List < MultiSound . Sound > soundsToPlay = new List < MultiSound . Sound > ( ) ;
bool tink = false ;
2023-10-29 19:44:47 +00:00
for ( float i = 0 ; i < length ; i + = 0.5f )
2023-03-07 17:22:32 +00:00
{
soundsToPlay . Add ( new MultiSound . Sound ( tink ? "rhythmRally/Tink" : "rhythmRally/Tonk" , beat + i ) ) ;
tink = ! tink ;
}
MultiSound . Play ( soundsToPlay . ToArray ( ) , forcePlay : true ) ;
}
2023-06-10 19:13:29 +00:00
public void SuperFastRallyStretchable ( double beat , float length )
2023-03-07 17:22:32 +00:00
{
List < BeatAction . Action > servesToPerform = new List < BeatAction . Action > ( ) ;
for ( int i = 0 ; i < length ; i + = 2 )
{
2023-06-10 19:13:29 +00:00
double beatToSpawn = beat + i ;
2023-10-29 19:44:47 +00:00
servesToPerform . Add ( new BeatAction . Action ( beatToSpawn , delegate { Serve ( beatToSpawn , RallySpeed . SuperFast ) ; } ) ) ;
2022-02-14 08:53:58 +00:00
}
2023-09-11 22:28:04 +00:00
BeatAction . New ( this , servesToPerform ) ;
2022-02-14 08:53:58 +00:00
}
2022-02-03 02:09:50 +00:00
}
}