mirror of
https://github.com/RHeavenStudioPlus/HeavenStudioPlus.git
synced 2024-11-15 22:25:07 +00:00
359 lines
15 KiB
C#
359 lines
15 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
|
||
|
using UnityEngine;
|
||
|
|
||
|
using HeavenStudio.Util;
|
||
|
using HeavenStudio.InputSystem;
|
||
|
|
||
|
using Jukebox;
|
||
|
|
||
|
namespace HeavenStudio.Games.Loaders
|
||
|
{
|
||
|
using static Minigames;
|
||
|
public static class NtrSlotMonsterLoader
|
||
|
{
|
||
|
public static Minigame AddGame(EventCaller eventCaller)
|
||
|
{
|
||
|
return new Minigame("slotMonster", "Slot Monster", "d10914", false, false, new List<GameAction>()
|
||
|
{
|
||
|
new GameAction("startInterval", "Start Interval")
|
||
|
{
|
||
|
function = delegate {
|
||
|
if (eventCaller.gameManager.minigameObj.TryGetComponent(out SlotMonster instance)) {
|
||
|
var e = eventCaller.currentEntity;
|
||
|
instance.StartInterval(e, e["auto"], e["eyeType"], 0);
|
||
|
}
|
||
|
},
|
||
|
defaultLength = 3f,
|
||
|
resizable = true,
|
||
|
preFunctionLength = 1,
|
||
|
priority = 2,
|
||
|
parameters = new List<Param>()
|
||
|
{
|
||
|
new Param("auto", true, "Auto Pass Turn", "Toggle if the turn should be passed automatically at the end of the start interval."),
|
||
|
new Param("eyeType", SlotMonster.EyeTypes.Random, "Eye Sprite", "Set the eye sprite to be used."),
|
||
|
},
|
||
|
},
|
||
|
new GameAction("slot", "Slot")
|
||
|
{
|
||
|
inactiveFunction = delegate {
|
||
|
// SoundByte.PlayOneShotGame("slotMonster/start_touch", eventCaller.currentEntity.beat, forcePlay: true);
|
||
|
SoundByte.PlayOneShotGame("slotMonster/start_touch", forcePlay: true);
|
||
|
},
|
||
|
defaultLength = 0.5f,
|
||
|
priority = 1,
|
||
|
parameters = new List<Param>()
|
||
|
{
|
||
|
new Param("drum", SlotMonster.DrumTypes.Default, "Drum SFX", "Set the drum SFX to be used. Default is Bass on the beat, and Snare off the beat.")
|
||
|
},
|
||
|
},
|
||
|
new GameAction("passTurn", "Pass Turn")
|
||
|
{
|
||
|
function = delegate {
|
||
|
if (eventCaller.gameManager.minigameObj.TryGetComponent(out SlotMonster instance)) {
|
||
|
var e = eventCaller.currentEntity;
|
||
|
instance.PassTurn(e.beat, e.length);
|
||
|
}
|
||
|
},
|
||
|
defaultLength = 1f,
|
||
|
priority = 1,
|
||
|
},
|
||
|
new GameAction("buttonColor", "Button Color")
|
||
|
{
|
||
|
function = delegate {
|
||
|
if (eventCaller.gameManager.minigameObj.TryGetComponent(out SlotMonster instance)) {
|
||
|
var e = eventCaller.currentEntity;
|
||
|
instance.ButtonColor(new Color[] { e["button1"], e["button2"], e["button3"] }, e["flash"]);
|
||
|
}
|
||
|
},
|
||
|
defaultLength = 1f,
|
||
|
priority = 1,
|
||
|
parameters = new List<Param>()
|
||
|
{
|
||
|
new Param("button1", new Color(0.38f, 0.98f, 0.25f), "Button 1 Color", "Set the color of the first button."),
|
||
|
new Param("button2", new Color(0.8f, 0.28f, 0.95f), "Button 2 Color", "Set the color of the second button."),
|
||
|
new Param("button3", new Color(0.87f, 0f, 0f), "Button 3 Color", "Set the color of the third button."),
|
||
|
new Param("flash", new Color(1f, 1f, 0.68f), "Button Flash Color", "Set the color of the flash of the buttons."),
|
||
|
},
|
||
|
},
|
||
|
new GameAction("gameplayModifiers", "Gameplay Modifiers")
|
||
|
{
|
||
|
function = delegate {
|
||
|
if (eventCaller.gameManager.minigameObj.TryGetComponent(out SlotMonster instance)) {
|
||
|
var e = eventCaller.currentEntity;
|
||
|
instance.GameplayModifiers(e["lottery"], e["lotteryAmount"], e["lotterySpeed"], e["stars"]);
|
||
|
}
|
||
|
},
|
||
|
defaultLength = 0.5f,
|
||
|
priority = 1,
|
||
|
parameters = new List<Param>()
|
||
|
{
|
||
|
new Param("lottery", true, "Lottery", "Toggle if the win particles should play after a successful sequence.", new() {
|
||
|
new Param.CollapseParam((x, _) => (bool)x, new string[] { "lotteryAmount", "lotterySpeed", "stars" }),
|
||
|
}),
|
||
|
new Param("lotteryAmount", new EntityTypes.Float(0, 10, 1), "Lottery Amount", "Set the amount of win particles."),
|
||
|
new Param("lotterySpeed", new EntityTypes.Float(0.5f, 3, 1), "Lottery Speed", "Set the speed of the win particles."),
|
||
|
new Param("stars", false, "Use Stars", "Use stars instead of coins? (From the Korean version of RH DS)"),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
new List<string>() { "ntr", "normal" },
|
||
|
"ntrslotmonster", "en",
|
||
|
new List<string>() {}
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace HeavenStudio.Games
|
||
|
{
|
||
|
using System.Linq;
|
||
|
using Scripts_SlotMonster;
|
||
|
public class SlotMonster : Minigame
|
||
|
{
|
||
|
public enum DrumTypes
|
||
|
{
|
||
|
Default,
|
||
|
Bass,
|
||
|
Snare,
|
||
|
}
|
||
|
|
||
|
public enum EyeTypes
|
||
|
{
|
||
|
Random,
|
||
|
Note,
|
||
|
Ring,
|
||
|
Cake,
|
||
|
Flower,
|
||
|
Beverage,
|
||
|
Mushroom,
|
||
|
Key,
|
||
|
Ribbon,
|
||
|
Hat,
|
||
|
Barista,
|
||
|
}
|
||
|
|
||
|
[Header("Animators")]
|
||
|
[SerializeField] Animator smAnim;
|
||
|
[SerializeField] Animator[] eyeAnims;
|
||
|
private int[] eyeSprites = new int[3];
|
||
|
|
||
|
[Header("Objects")]
|
||
|
[SerializeField] SlotButton[] buttons;
|
||
|
[SerializeField] ParticleSystem winParticles;
|
||
|
public Color buttonFlashColor = new(1f, 1f, 0.68f);
|
||
|
|
||
|
private List<RiqEntity> gameEntities;
|
||
|
private bool doWin = true;
|
||
|
private bool inputsActive = false;
|
||
|
|
||
|
private Sound rollingSound;
|
||
|
private int currentEyeSprite = 1;
|
||
|
private int maxButtons;
|
||
|
private int currentButton;
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
foreach (var button in buttons) {
|
||
|
button.Init(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnPlay(double beat)
|
||
|
{
|
||
|
OnGameSwitch(beat);
|
||
|
}
|
||
|
|
||
|
public override void OnGameSwitch(double beat)
|
||
|
{
|
||
|
gameEntities = gameManager.Beatmap.Entities.FindAll(c => c.datamodel.Split('/')[0] == "slotMonster");
|
||
|
foreach (RiqEntity e in gameEntities.FindAll(e => e.datamodel == "slotMonster/startInterval" && e.beat < beat && e.beat + e.length > beat)) {
|
||
|
StartInterval(e, e["auto"], e["eyeType"], beat);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void Update()
|
||
|
{
|
||
|
if (PlayerInput.GetIsAction(InputAction_BasicPress) && !IsExpectingInputNow(InputAction_BasicPress) && inputsActive && !buttons[currentButton].pressed) {
|
||
|
HitButton();
|
||
|
ScoreMiss();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void LateUpdate()
|
||
|
{
|
||
|
currentButton = Array.FindIndex(buttons, button => !button.pressed);
|
||
|
}
|
||
|
|
||
|
private void HitButton(bool isHit = false, int timing = 0)
|
||
|
{
|
||
|
bool isLast = currentButton == maxButtons - 1;
|
||
|
buttons[currentButton].Press(!isHit || timing != 0);
|
||
|
for (int i = currentButton; i < (isLast ? 3 : currentButton + 1); i++)
|
||
|
{
|
||
|
if (eyeAnims[i].IsPlayingAnimationNames("Spin")) {
|
||
|
int eyeSprite = currentEyeSprite;
|
||
|
string anim = "EyeItem";
|
||
|
if (!isHit) {
|
||
|
do {
|
||
|
eyeSprite = UnityEngine.Random.Range(1, 10);
|
||
|
} while (eyeSprite == currentEyeSprite || eyeSprites.Contains(eyeSprite));
|
||
|
|
||
|
eyeSprites[i] = eyeSprite;
|
||
|
anim += eyeSprite;
|
||
|
} else { // only do all this if it's actually a hit
|
||
|
if (timing == -1) { // if the timing is early
|
||
|
eyeSprite = (eyeSprite + 1) % 9;
|
||
|
}
|
||
|
anim += eyeSprite + 1;
|
||
|
if (timing != 0) { // if it's a barely
|
||
|
anim += "Barely";
|
||
|
}
|
||
|
}
|
||
|
eyeAnims[i].Play(anim, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
bool isMiss = buttons.Any(x => x.missed);
|
||
|
string hitSfx = "slotMonster/stop_" + (isLast && isHit && !isMiss ? "hit" : (currentButton + 1));
|
||
|
SoundByte.PlayOneShotGame(hitSfx, forcePlay: true);
|
||
|
if (isLast) {
|
||
|
if (rollingSound != null) rollingSound.Stop();
|
||
|
inputsActive = false;
|
||
|
if (isHit && !isMiss) {
|
||
|
smAnim.DoScaledAnimationAsync("Win", 0.5f);
|
||
|
if (doWin) {
|
||
|
SoundByte.PlayOneShotGame("slotMonster/win");
|
||
|
winParticles.Play();
|
||
|
}
|
||
|
} else {
|
||
|
smAnim.DoScaledAnimationAsync("Lose", 0.5f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void StartInterval(RiqEntity si, bool autoPass, int eyeSprite, double gameSwitchBeat)
|
||
|
{
|
||
|
if (rollingSound != null) rollingSound.Stop();
|
||
|
List<RiqEntity> slotActions = gameEntities.FindAll(e => e.datamodel == "slotMonster/slot" && e.beat >= si.beat && e.beat < si.beat + si.length);
|
||
|
if (slotActions.Count <= 0) return;
|
||
|
|
||
|
SoundByte.PlayOneShotGame("slotMonster/start_touch", forcePlay: true);
|
||
|
smAnim.DoScaledAnimationFromBeatAsync("Prepare", 0.5f, si.beat);
|
||
|
|
||
|
List<MultiSound.Sound> sounds = new();
|
||
|
List<BeatAction.Action> actions = new();
|
||
|
maxButtons = Mathf.Min(slotActions.Count, 3);
|
||
|
for (int i = 0; i < maxButtons; i++) // limit to 3 actions
|
||
|
{
|
||
|
buttons[i].Ready();
|
||
|
eyeAnims[i].Play("Idle", 0, 1);
|
||
|
int whichSlot = i;
|
||
|
RiqEntity slot = slotActions[whichSlot];
|
||
|
if (slot.beat < gameSwitchBeat) continue;
|
||
|
string sfx = "";
|
||
|
if (slot["drum"] == (int)DrumTypes.Default) {
|
||
|
sfx = slot.beat % 1 == 0 ? "bass" : "snare";
|
||
|
} else {
|
||
|
sfx = Enum.GetName(typeof(DrumTypes), (int)slot["drum"]).ToLower();
|
||
|
}
|
||
|
sounds.Add(new(sfx + "DrumNTR", slot.beat));
|
||
|
actions.Add(new(slot.beat, delegate {
|
||
|
buttons[whichSlot].TryFlash();
|
||
|
}));
|
||
|
}
|
||
|
MultiSound.Play(sounds.ToArray(), false);
|
||
|
BeatAction.New(this, actions);
|
||
|
|
||
|
if (autoPass) {
|
||
|
BeatAction.New(this, new() { new(si.beat + si.length, delegate {
|
||
|
currentEyeSprite = eyeSprite == 0 ? UnityEngine.Random.Range(1, 10) : eyeSprite - 1;
|
||
|
PassTurn(si.beat + si.length, 1, si.beat, si, slotActions);
|
||
|
})});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void PassTurn(double beat, float length, double startBeat = -1, RiqEntity startInterval = null, List<RiqEntity> slotActions = null)
|
||
|
{
|
||
|
smAnim.DoScaledAnimationFromBeatAsync("Release", 0.5f, beat);
|
||
|
for (int i = 0; i < eyeAnims.Length; i++) {
|
||
|
eyeAnims[i].DoScaledAnimationAsync("Spin", 0.5f);
|
||
|
}
|
||
|
SoundByte.PlayOneShotGame("slotMonster/start_rolling", forcePlay: true);
|
||
|
rollingSound = SoundByte.PlayOneShotGame("slotMonster/rolling", looping: true, forcePlay: true);
|
||
|
|
||
|
startInterval ??= gameEntities.FindLast(e => e.datamodel == "slotMonster/startInterval" && e.beat + e.length < beat);
|
||
|
if (startBeat < 0) {
|
||
|
startBeat = startInterval.beat;
|
||
|
}
|
||
|
slotActions ??= gameEntities.FindAll(e => e.datamodel == "slotMonster/slot" && e.beat >= startInterval.beat && e.beat < startInterval.beat + startInterval.length);
|
||
|
|
||
|
List<BeatAction.Action> actions = new() {
|
||
|
new(beat, delegate { inputsActive = true; })
|
||
|
};
|
||
|
for (int i = 0; i < Mathf.Min(slotActions.Count, 3); i++) // limit to 3 actions
|
||
|
{
|
||
|
int whichSlot = i;
|
||
|
double slotBeat = slotActions[i].beat;
|
||
|
|
||
|
actions.Add(new(beat + length + slotBeat - startBeat, delegate {
|
||
|
buttons[whichSlot].TryFlash();
|
||
|
}));
|
||
|
|
||
|
buttons[whichSlot].input = ScheduleInput(beat, slotBeat - startBeat + length, InputAction_BasicPress, ButtonHit, i == slotActions.Count - 1 ? ButtonEndMiss : null, null, () => {
|
||
|
return currentButton == whichSlot && !buttons[whichSlot].pressed;
|
||
|
});
|
||
|
}
|
||
|
BeatAction.New(this, actions);
|
||
|
}
|
||
|
|
||
|
private void ButtonHit(PlayerActionEvent caller, float state)
|
||
|
{
|
||
|
int timing = state switch {
|
||
|
>= 1f => -1,
|
||
|
<= -1f => 1,
|
||
|
_ => 0,
|
||
|
};
|
||
|
HitButton(true, timing);
|
||
|
if (state is >= 1f or <= -1f) SoundByte.PlayOneShot("nearMiss");
|
||
|
}
|
||
|
|
||
|
private void ButtonEndMiss(PlayerActionEvent caller)
|
||
|
{
|
||
|
Debug.Log("miss i guess");
|
||
|
if (rollingSound != null) rollingSound.Stop();
|
||
|
inputsActive = false;
|
||
|
smAnim.DoScaledAnimationAsync("Lose", 0.5f);
|
||
|
foreach (var anim in eyeAnims) {
|
||
|
if (anim.IsPlayingAnimationNames("Spin")) {
|
||
|
anim.Play("Idle", 0, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void ButtonColor(Color[] colors, Color flashColor)
|
||
|
{
|
||
|
for (int i = 0; i < buttons.Length; i++) {
|
||
|
buttons[i].color = colors[i];
|
||
|
}
|
||
|
buttonFlashColor = flashColor;
|
||
|
}
|
||
|
|
||
|
public void GameplayModifiers(bool lottery, float amount, float speed, bool stars)
|
||
|
{
|
||
|
var sheetAnim = winParticles.textureSheetAnimation;
|
||
|
sheetAnim.frameOverTime = stars ? 1 : 0;
|
||
|
|
||
|
var rotOverTime = winParticles.rotationOverLifetime;
|
||
|
rotOverTime.z = new ParticleSystem.MinMaxCurve(stars ? -5 : 0, stars ? 5 : 0);
|
||
|
|
||
|
var emission = winParticles.emission;
|
||
|
emission.rateOverTime = amount * 13;
|
||
|
|
||
|
var main = winParticles.main;
|
||
|
main.simulationSpeed = speed;
|
||
|
|
||
|
doWin = lottery;
|
||
|
}
|
||
|
}
|
||
|
}
|