Meat Grinder Rework (#598)

* meat grinder has been entirely reworked

* update MeatGrinder.cs, replace assets

background is still temporary but this is all im doing til ev can help

* Boss New Anims

* New Tack Animations

* edited anims

* new bg

* working out merge conflics (cries)

* push for anims stuff

why r unity animations so suck

* Cart guy

cart guy

cart guy

* cart guy :)

* explodes

👼

* curves for meat trajectory

* typo

* cart guy is awesome. and tack reaction.

🐱 🐱 🐱 🐱 🐱

* I love unity animation

* everything but the boss call cancel fix

* bacon anim (+ remove interp on other meat anims)

* lastbeatpulse + gears are the only thing left

i was GONNA add both the beat based and constant gear animations but it just doesn't work out the way i want it to.
OHHHH WELLLLL
im gonna use straight up transforms anyways so i wont have to deal with unity animation (🤢)

* truly, and dearly, finished?

NOPE! UNITY ANIMATION BUG :D

* mwuhahahaha

disable ass buns

* meatsplash start, fix riqentity find bug

* fixes + particle improvements

all that's left is the phone bop and arc/particle adjustments, im pretty sure

* particle + arc adjustment, fix weird anim play on gameswitch

* fixed phonebop

oops i was playing the idle animation in update...

* make particles beat based

just waiting on mine to fix the offset bug

* fix trajectory jank

---------

Co-authored-by: Rapandrasmus <78219215+Rapandrasmus@users.noreply.github.com>
Co-authored-by: Seanski2 <seanbenedit@gmail.com>
Co-authored-by: ev <85412919+evdial@users.noreply.github.com>
Co-authored-by: minenice55 <star.elementa@gmail.com>
This commit is contained in:
AstrlJelly
2023-12-30 23:06:31 -05:00
committed by GitHub
parent 8b6d2f63b8
commit a1543ce3ef
87 changed files with 30820 additions and 9221 deletions

View File

@ -644,6 +644,16 @@ namespace HeavenStudio
private void LateUpdate()
{
OverlaysManager.instance.TogleOverlaysVisibility(Editor.Editor.instance == null || Editor.Editor.instance.fullscreen || ((PersistentDataManager.gameSettings.overlaysInEditor) && (!Editor.Editor.instance.fullscreen)) || HeavenStudio.Editor.GameSettings.InPreview);
if (!Conductor.instance.isPlaying)
return;
if (Conductor.instance.songPositionInBeatsAsDouble >= Math.Ceiling(_playStartBeat) + _latePulseTally)
{
if (_currentMinigame != null) _currentMinigame.OnLateBeatPulse(Math.Ceiling(_playStartBeat) + _latePulseTally);
onBeatPulse?.Invoke(Math.Ceiling(_playStartBeat) + _latePulseTally);
_latePulseTally++;
}
}
public void ToggleInputs(bool inputs)
@ -655,6 +665,7 @@ namespace HeavenStudio
private double _playStartBeat = 0;
private int _pulseTally = 0;
private int _latePulseTally = 0;
public void Play(double beat, float delay = 0f)
{
@ -662,6 +673,7 @@ namespace HeavenStudio
Debug.Log("Playing at " + beat);
_playStartBeat = beat;
_pulseTally = 0;
_latePulseTally = 0;
canInput = true;
if (!paused)
{

View File

@ -0,0 +1,223 @@
using HeavenStudio.Util;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace HeavenStudio.Games.Scripts_MeatGrinder
{
public class Meat : MonoBehaviour
{
[SerializeField] Transform startPosition;
[SerializeField] Transform startPositionAlt;
[SerializeField] Transform hitPosition;
[SerializeField] Transform missPosition;
[SerializeField] float meatFlyHeight = 1f;
[SerializeField] float meatFlyHeightAlt = 1f;
[SerializeField] bool showAltCurve;
[NonSerialized] public double startBeat;
[NonSerialized] public MeatType meatType;
[NonSerialized] public MeatGrinder.Reaction tackReaction;
[NonSerialized] public MeatGrinder.Reaction bossReaction;
private bool isHit = false;
// const float meatStart = 0;
// const float meatEnd = 3.43f;
[Header("Animators")]
private Animator anim;
private SpriteRenderer sr;
[SerializeField] private Sprite[] meats;
public enum MeatType
{
DarkMeat,
LightMeat,
BaconBall,
}
private MeatGrinder game;
private void Awake()
{
game = MeatGrinder.instance;
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
// anim.writeDefaultValuesOnDisable = false;
}
private void Start()
{
sr.sprite = meats[(int)meatType];
game.ScheduleInput(startBeat, 1, MeatGrinder.InputAction_Press, Hit, Miss, Nothing);
}
private void Update()
{
Conductor cond = Conductor.instance;
if (!isHit)
{
double startTime = cond.GetSongPosFromBeat(startBeat);
double hitTime = cond.GetSongPosFromBeat(startBeat + 1);
double missTime = hitTime + MeatGrinder.ngLateTime;
double currentTime = cond.songPositionAsDouble;
Vector3 lastPos = transform.position;
Vector3 startPos = meatType == MeatType.LightMeat ? startPositionAlt.position : startPosition.position;
float hitAlongMissRatio = Vector3.Dot((startPos - missPosition.position), (startPos - hitPosition.position));
hitAlongMissRatio /= Vector3.Dot((startPos - missPosition.position), (startPos - missPosition.position));
Vector3 hitOnMissPos = startPos + ((missPosition.position - startPos) * hitAlongMissRatio);
float prog;
if (currentTime >= hitTime)
{
prog = (float)((currentTime - hitTime) / (missTime - hitTime));
transform.position = Vector3.Lerp(hitOnMissPos, missPosition.position, prog);
prog = (prog * (1 - hitAlongMissRatio)) + hitAlongMissRatio;
}
else
{
prog = (float)((currentTime - startTime) / (hitTime - startTime));
transform.position = Vector3.Lerp(startPos, hitOnMissPos, prog);
prog *= hitAlongMissRatio;
}
float yMul = prog * 2f - 1f;
float yWeight = -(yMul * yMul) + 1f;
transform.position += (meatType == MeatType.LightMeat ? meatFlyHeightAlt : meatFlyHeight) * yWeight * Vector3.up;
// point towards the next position
if (cond.isPlaying)
{
transform.right = transform.position - lastPos;
}
}
}
private void Hit(PlayerActionEvent caller, float state)
{
anim.enabled = true;
isHit = true;
game.TackAnim.SetBool("tackMeated", false);
anim.DoScaledAnimationAsync(meatType.ToString() + "Hit", 0.5f);
bool isBarely = state is >= 1f or <= -1f;
game.bossAnnoyed = isBarely;
SoundByte.PlayOneShotGame("meatGrinder/" + (isBarely ? "tink" : "meatHit"));
game.TackAnim.DoScaledAnimationAsync("TackHit" + (isBarely ? "Barely" : "Success"), 0.5f);
(float rangeStart, float rangeEnd) = meatType switch { // im not good enough at math to figure out how to make this an equation
MeatType.DarkMeat => (0, 0.333f),
MeatType.LightMeat => (0.334f, 0.666f),
MeatType.BaconBall or _ => (0.667f, 1),
};
var sheetAnim = game.MeatSplash.textureSheetAnimation;
var main = game.MeatSplash.main;
sheetAnim.frameOverTime = new ParticleSystem.MinMaxCurve(rangeStart, rangeEnd);
main.simulationSpeed = 0.5f / Conductor.instance.pitchedSecPerBeat;
// has a probability of zero normally so it's not played with Play() but this exposes it to the editor
var emission = game.MeatSplash.emission;
game.MeatSplash.Emit(new ParticleSystem.EmitParams(), (int)emission.GetBurst(0).count.constant);
if (tackReaction.expression > 0) {
BeatAction.New(game, new List<BeatAction.Action>() {
new(startBeat + tackReaction.beat + 1, delegate { game.DoExpressions(tackReaction.expression); }),
});
}
if (bossReaction.expression > 0) {
BeatAction.New(game, new List<BeatAction.Action>() {
new(startBeat + bossReaction.beat + 1, delegate { game.DoExpressions(0, bossReaction.expression); }),
});
}
}
private void Miss(PlayerActionEvent caller)
{
anim.enabled = true;
game.bossAnnoyed = true;
SoundByte.PlayOneShotGame("meatGrinder/miss");
game.TackAnim.DoScaledAnimationAsync("TackMiss" + meatType.ToString(), 0.5f);
game.TackAnim.SetBool("tackMeated", true);
game.BossAnim.DoScaledAnimationAsync("BossMiss", 0.5f);
Destroy(gameObject);
}
private void Nothing(PlayerActionEvent caller) { }
public void SetSprite()
{
sr.sprite = meats[(int)meatType];
}
public void DestroySelf()
{
Destroy(gameObject);
}
/// <summary>
/// Callback to draw gizmos that are pickable and always drawn.
/// </summary>
private void OnDrawGizmos()
{
// draw a line showing the direction from the miss position to the hit position
if (startPosition == null) return;
if (startPositionAlt == null) return;
Vector3 startPos = showAltCurve ? startPositionAlt.position : startPosition.position;
if (hitPosition != null && missPosition != null && startPos != null)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(startPos, hitPosition.position);
Gizmos.color = Color.blue;
Gizmos.DrawLine(startPos, missPosition.position);
Gizmos.color = Color.green;
Vector3 direction = (hitPosition.position - missPosition.position).normalized;
Gizmos.DrawRay(missPosition.position, direction * 10);
// project start position -> hit position onto start position -> miss position
float hitAlongMissRatio = Vector3.Dot((startPos - missPosition.position), (startPos - hitPosition.position));
hitAlongMissRatio /= Vector3.Dot((startPos - missPosition.position), (startPos - missPosition.position));
Gizmos.color = Color.yellow;
Gizmos.DrawRay(startPos, (missPosition.position - startPos) * hitAlongMissRatio);
DrawCurveGizmo(startPos, missPosition.position, Vector3.up * (showAltCurve ? meatFlyHeightAlt : meatFlyHeight), Color.yellow, 0.1f);
}
}
void DrawCurveGizmo(Vector3 start, Vector3 end, Vector3 height, Color colour, float interval)
{
Gizmos.color = colour;
Vector3 lastPos = start;
Vector3 pos;
float t, yMul, yWeight;
for (float i = 0; i < 1; i += interval)
{
t = i;
pos = Vector3.Lerp(start, end, t);
yMul = t * 2f - 1f;
yWeight = -(yMul * yMul) + 1f;
pos += height * yWeight;
Gizmos.DrawLine(lastPos, pos);
lastPos = pos;
}
t = 1f;
pos = end;
yMul = t * 2f - 1f;
yWeight = -(yMul * yMul) + 1f;
pos += height * yWeight;
Gizmos.DrawLine(lastPos, pos);
}
}
}

View File

@ -3,51 +3,28 @@ using HeavenStudio.InputSystem;
using System;
using System.Collections.Generic;
using UnityEngine;
using NaughtyBezierCurves;
using System.Linq;
namespace HeavenStudio.Games.Loaders
{
using static Minigames;
public static class pcoMeatLoader
public static class PcoMeatLoader
{
public static Minigame AddGame(EventCaller eventCaller)
{
List<Param> reactionParams = new() {
new Param("tackReaction", MeatGrinder.TackExpressions.None, "Tack Reaction", "If this is hit, what expression should tack do?", new List<Param.CollapseParam>() {
new((x, y) => (int)x != (int)MeatGrinder.TackExpressions.None, new string[] { "tackReactionBeats" }),
}),
new Param("tackReactionBeats", new EntityTypes.Float(0.5f, 10, 1), "Tack React After", "The amount of beats to wait until tack reacts"),
new Param("bossReaction", MeatGrinder.BossExpressions.None, "Boss Reaction", "If this is hit, what expression should boss do?", new List<Param.CollapseParam>() {
new((x, y) => (int)x != (int)MeatGrinder.BossExpressions.None, new string[] { "bossReactionBeats" }),
}),
new Param("bossReactionBeats", new EntityTypes.Float(0, 10, 0), "Boss React After", "The amount of beats to wait until boss reacts"),
};
return new Minigame("meatGrinder", "Meat Grinder", "501d18", false, false, new List<GameAction>()
{
new GameAction("MeatToss", "Meat Toss")
{
function = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.instance.MeatToss(e.beat);
},
defaultLength = 2f,
priority = 2,
},
new GameAction("MeatCall", "Meat Call")
{
function = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.instance.MeatCall(e.beat);
},
defaultLength = 0.5f,
priority = 2,
preFunctionLength = 1f,
preFunction = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.PreInterval(e.beat, 4f);
},
},
new GameAction("StartInterval", "Start Interval")
{
defaultLength = 4f,
resizable = true,
priority = 5,
preFunction = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.PreInterval(e.beat, e.length);
},
},
new GameAction("bop", "Bop")
{
function = delegate {
@ -62,10 +39,95 @@ namespace HeavenStudio.Games.Loaders
resizable = true,
priority = 1,
},
},
new List<string>() { "pco", "normal", "repeat" },
"pcomeat", "en",
new List<string>() { }
new GameAction("MeatToss", "Meat Toss")
{
function = delegate {
var e = eventCaller.currentEntity;
SoundByte.PlayOneShotGame("meatGrinder/toss", forcePlay: true);
MeatGrinder.instance.MeatToss(e.beat, e["bacon"], e["tackReaction"], e["tackReactionBeats"], e["bossReaction"], e["bossReactionBeats"]);
},
inactiveFunction = delegate {
SoundByte.PlayOneShotGame("meatGrinder/toss", forcePlay: true);
MeatGrinder.QueueMeatToss(eventCaller.currentEntity);
},
defaultLength = 2f,
priority = 2,
parameters = new List<Param>()
{
new Param("bacon", false, "Bacon Ball", "Throw a bacon ball instead of the typical meat"),
}.Concat(reactionParams).ToList(), // doing this because i want these params to always be the same
},
new GameAction("StartInterval", "Start Interval")
{
defaultLength = 4f,
resizable = true,
preFunction = delegate
{
var e = eventCaller.currentEntity;
MeatGrinder.PreInterval(e.beat, e.length, e["auto"]);
},
parameters = new List<Param>()
{
new Param("auto", true, "Auto Pass Turn")
},
preFunctionLength = 1
},
new GameAction("MeatCall", "Meat Call")
{
inactiveFunction = delegate { SoundByte.PlayOneShotGame("meatGrinder/signal", forcePlay: true); },
defaultLength = 0.5f,
priority = 2,
preFunctionLength = 1f,
parameters = reactionParams,
},
new GameAction("passTurn", "Pass Turn")
{
preFunction = delegate { MeatGrinder.PrePassTurn(eventCaller.currentEntity.beat); },
preFunctionLength = 1
},
new GameAction("expressions", "Expressions")
{
function = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.instance.DoExpressions(e["tackExpression"], e["bossExpression"]);
},
parameters = new List<Param>() {
new Param("tackExpression", MeatGrinder.TackExpressions.Content, "Tack Expression", "The expression Tack will display"),
new Param("bossExpression", MeatGrinder.BossExpressions.None, "Boss Expression", "The expression Boss will display"),
}
},
new GameAction("cartGuy", "Cart Guy")
{
function = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.instance.CartGuy(e.beat, e.length, e["spider"], e["direction"], e["ease"]);
},
resizable = true,
defaultLength = 16,
parameters = new List<Param>() {
new Param("spider", false, "On Phone", "Put a spider in the box?"),
new Param("direction", MeatGrinder.CartGuyDirection.Right, "Direction", "The direction the cart will be carted to."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "What ease will the cart use?"),
}
},
new GameAction("gears", "Gears")
{
function = delegate {
var e = eventCaller.currentEntity;
MeatGrinder.instance.ChangeGears(e.beat, e.length, e["ease"], e["speed"]);
},
resizable = true,
defaultLength = 1,
parameters = new List<Param>() {
new Param("speed", new EntityTypes.Float(0, 10, 1), "Speed", "How fast will the gears go?"),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "What ease will the gears speed up/slow down with?"),
}
},
}
// ,
// new List<string>() { "pco", "normal", "repeat" },
// "pcomeat", "en",
// new List<string>() { }
);
}
}
@ -73,37 +135,82 @@ namespace HeavenStudio.Games.Loaders
namespace HeavenStudio.Games
{
using Jukebox;
using Scripts_MeatGrinder;
public class MeatGrinder : Minigame
{
static List<double> queuedInputs = new();
static List<QueuedInterval> queuedIntervals = new List<QueuedInterval>();
struct QueuedInterval
private static List<QueuedInterval> queuedIntervals = new();
private struct QueuedInterval
{
public double beat;
public double length;
public float length;
public bool autoPassTurn;
}
public struct UpdateEasing
{
public double beat;
public float length;
public Util.EasingFunction.Ease ease;
}
public struct Reaction
{
public int expression;
public double beat;
public Reaction(int expression, double beat) {
this.expression = expression;
this.beat = beat;
}
}
// private static List<RiqEntity> queuedMeats = new();
[Header("Objects")]
public GameObject MeatBase;
public ParticleSystem MeatSplash;
[SerializeField] Transform[] Gears;
[Header("Animators")]
public Animator BossAnim;
public Animator TackAnim;
[SerializeField] Animator CartGuyParentAnim;
[SerializeField] Animator CartGuyAnim;
[Header("Variables")]
bool intervalStarted;
double intervalStartBeat;
public double beatInterval = 4f;
private bool bossBop = true;
public bool bossAnnoyed = false;
const string sfxName = "meatGrinder/";
private UpdateEasing cartEase;
private bool cartPhone = false;
private string cartDir = "Left";
private UpdateEasing gearEase;
private float oldGearSpeed = 1;
private float newGearSpeed = 1;
private const string sfxName = "meatGrinder/";
public static MeatGrinder instance;
public enum MeatType
public enum TackExpressions
{
Dark,
Light,
None,
Content,
Smug,
Wonder,
}
public enum BossExpressions
{
None,
Eyebrow,
Scared,
}
public enum CartGuyDirection
{
Right,
Left,
}
protected static bool IA_PadAny(out double dt)
@ -116,147 +223,292 @@ namespace HeavenStudio.Games
}
public static PlayerInput.InputAction InputAction_Press =
new("PcoMeatPress", new int[] { IAPressCat, IAFlickCat, IAPressCat },
new("PcoMeatPress", new int[] { IAPressCat, IAPressCat, IAPressCat },
IA_PadAny, IA_TouchBasicPress, IA_BatonBasicPress);
private void Awake()
{
instance = this;
SetupBopRegion("meatGrinder", "bop", "bossBop");
}
void OnDestroy()
{
if (!Conductor.instance.isPlaying || Conductor.instance.isPaused)
{
if (queuedInputs.Count > 0) queuedInputs.Clear();
if (queuedIntervals.Count > 0) queuedIntervals.Clear();
intervalStarted = false;
beatInterval = 4f;
}
foreach (var evt in scheduledInputs) evt.Disable();
MeatBase.SetActive(false);
}
private void Update()
{
var cond = Conductor.instance;
if (PlayerInput.GetIsAction(InputAction_Press) && !IsExpectingInputNow(InputAction_Press))
{
TackAnim.DoScaledAnimationAsync("TackEmptyHit", 0.5f);
TackAnim.SetBool("tackMeated", false);
SoundByte.PlayOneShotGame(sfxName + "whiff");
SoundByte.PlayOneShotGame("meatGrinder/whiff");
bossAnnoyed = false;
}
if (bossAnnoyed) BossAnim.SetBool("bossAnnoyed", true);
if (queuedIntervals.Count > 0)
if (passedTurns.Count > 0)
{
foreach (var interval in queuedIntervals) StartInterval(interval.beat, interval.length);
queuedIntervals.Clear();
foreach (double pass in passedTurns)
{
PassTurnStandalone(pass);
}
passedTurns.Clear();
}
var currentGearSpeed = newGearSpeed;
if (gearEase.length != 0)
{
float normalizedBeat = cond.GetPositionFromBeat(gearEase.beat, gearEase.length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(gearEase.ease);
currentGearSpeed = func(oldGearSpeed, newGearSpeed, normalizedBeat);
if (normalizedBeat >= 1) cartEase.length = 0;
}
if (cartEase.length != 0)
{
float normalizedBeat = cond.GetPositionFromBeat(cartEase.beat, cartEase.length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(cartEase.ease);
float newPos = func(0f, 1f, normalizedBeat);
CartGuyParentAnim.DoNormalizedAnimation($"Move{cartDir}", newPos);
if (normalizedBeat >= 1) cartEase.length = 0;
}
CartGuyParentAnim.gameObject.SetActive(cartEase.length != 0);
if (cond.isPlaying && !cond.isPaused) {
foreach (Transform gear in Gears) {
double newZ = Time.deltaTime * currentGearSpeed * 50 * (gear.name == "Big" ? -1 : 1) / cond.pitchedSecPerBeat;
gear.Rotate(new Vector3(0, 0, (float)newZ));
}
}
if (cond.isPlaying) {
MeatSplash.Play();
} else if (cond.isPaused) {
MeatSplash.Pause();
} else {
MeatSplash.Stop();
}
}
public override void OnBeatPulse(double beat)
public override void OnLateBeatPulse(double beat)
{
if (!BossAnim.IsPlayingAnimationNames("BossCall")
&& !BossAnim.IsPlayingAnimationNames("BossSignal")
&& BeatIsInBopRegion(beat))
if (!BossAnim.IsPlayingAnimationNames("BossCall", "BossSignal", "BossScared") && BeatIsInBopRegion(beat))
{
BossAnim.DoScaledAnimationAsync(bossAnnoyed ? "BossMiss" : "Bop", 0.5f);
}
}
public void Bop(double beat, double length, bool doesBop, bool autoBop)
{
if (doesBop)
{
for (int i = 0; i < length; i++)
{
BeatAction.New(instance, new List<BeatAction.Action>() {
new BeatAction.Action(beat + i, delegate {
if (!BossAnim.IsPlayingAnimationNames("BossCall") && !BossAnim.IsPlayingAnimationNames("BossSignal")) {
BossAnim.DoScaledAnimationAsync(bossAnnoyed ? "BossMiss" : "Bop", 0.5f);
}
})
});
if (CartGuyParentAnim.gameObject.activeSelf) {
Debug.Log(cartPhone ? "PhoneBop" : "Bop");
if (cartPhone) {
CartGuyAnim.DoScaledAnimationAsync("PhoneBop", 0.5f);
} else {
CartGuyAnim.DoScaledAnimationAsync("Bop", 0.5f);
}
}
}
public static void PreInterval(double beat, double length)
public override void OnGameSwitch(double beat)
{
if (MeatGrinder.instance.intervalStarted || MeatGrinder.queuedIntervals.Count > 0) return;
MeatGrinder.queuedIntervals.Add(new QueuedInterval()
if (queuedIntervals.Count > 0)
{
foreach (var interval in queuedIntervals) StartInterval(interval.beat, interval.length, beat, interval.autoPassTurn);
queuedIntervals.Clear();
}
// if (queuedMeats.Count > 0)
// {
// foreach (var meat in queuedMeats) MeatToss(meat.beat, meat["bacon"], meat["tackReaction"], meat["tackReactionBeats"], meat["bossReaction"], meat["bossReactionBeats"]);
// queuedMeats.Clear();
// }
OnPlay(beat);
}
public override void OnPlay(double beat)
{
List<RiqEntity> allEntities = GameManager.instance.Beatmap.Entities.FindAll(c => c.datamodel.Split('/')[0] == "meatGrinder");
RiqEntity cg = allEntities.Find(c => c.datamodel == "meatGrinder/cartGuy");
if (cg != null) {
CartGuy(cg.beat, cg.length, cg["spider"], cg["direction"], cg["ease"]);
}
RiqEntity gr = allEntities.Find(c => c.datamodel == "meatGrinder/gears");
if (gr != null) {
ChangeGears(gr.beat, gr.length, gr["ease"], gr["speed"]);
}
List<RiqEntity> meats = allEntities.FindAll(c => c.datamodel == "meatGrinder/MeatToss" && beat > c.beat && beat < c.beat + 1);
foreach (var meat in meats) {
MeatToss(meat.beat, meat["bacon"], meat["tackReaction"], meat["tackReactionBeats"], meat["bossReaction"], meat["bossReactionBeats"]);
}
}
private List<RiqEntity> GetRelevantMeatCallsBetweenBeat(double beat, double endBeat)
{
return EventCaller.GetAllInGameManagerList("meatGrinder", new string[] { "MeatCall" }).FindAll(x => x.beat >= beat && x.beat < endBeat);
}
public void Bop(double beat, double length, bool doesBop, bool autoBop)
{
bossBop = autoBop;
if (doesBop)
{
var actions = new List<BeatAction.Action>();
for (int i = 0; i < length; i++)
{
actions.Add(new BeatAction.Action(beat + i, delegate {
if (!BossAnim.IsPlayingAnimationNames("BossCall", "BossSignal")) {
BossAnim.DoScaledAnimationAsync(bossAnnoyed ? "BossMiss" : "Bop", 0.5f);
}
}));
}
BeatAction.New(instance, actions);
}
}
public void DoExpressions(int tackExpression, int bossExpression = 0)
{
if (tackExpression != (int)TackExpressions.None) {
string tackAnim = ((TackExpressions)tackExpression).ToString();
TackAnim.DoScaledAnimationAsync("Tack" + tackAnim, 0.5f);
}
if (bossExpression != (int)BossExpressions.None) {
string bossAnim = ((BossExpressions)bossExpression).ToString();
BossAnim.DoScaledAnimationAsync("Boss" + bossAnim, 0.5f);
}
}
public void CartGuy(double beat, float length, bool spider, int direction, int ease)
{
cartEase = new() {
beat = beat,
length = length,
});
ease = (Util.EasingFunction.Ease)ease,
};
cartPhone = spider;
cartDir = direction == 0 ? "Right" : "Left";
if (cartPhone) {
CartGuyAnim.Play("Phone", 0, 0);
}
}
public void ChangeGears(double beat, float length, int ease, float speed)
{
gearEase = new() {
beat = beat,
length = length,
ease = (Util.EasingFunction.Ease)ease,
};
oldGearSpeed = newGearSpeed;
newGearSpeed = speed;
}
public static void PreInterval(double beat, float length, bool autoPassTurn)
{
SoundByte.PlayOneShotGame("meatGrinder/startSignal", beat - 1, forcePlay: true);
if (GameManager.instance.currentGame == "meatGrinder")
{
BeatAction.New(MeatGrinder.instance, new List<BeatAction.Action>() {
new BeatAction.Action(beat - 1, delegate {
MeatGrinder.instance.BossAnim.DoScaledAnimationAsync("BossSignal", 0.5f);
}),
});
instance.StartInterval(beat, length, beat, autoPassTurn);
}
}
public void StartInterval(double beat, double length)
{
if (MeatGrinder.instance.intervalStarted) return;
intervalStartBeat = beat;
intervalStarted = true;
beatInterval = length;
BeatAction.New(this, new List<BeatAction.Action>() {
new BeatAction.Action(beat + length - 0.33f, delegate { PassTurn(beat); }),
});
}
public void MeatToss(double beat)
{
SoundByte.PlayOneShotGame(sfxName + "toss");
MeatToss Meat = Instantiate(MeatBase, gameObject.transform).GetComponent<MeatToss>();
Meat.startBeat = beat;
Meat.cueLength = 1f;
Meat.cueBased = true;
Meat.meatType = "DarkMeat";
}
public void MeatCall(double beat)
{
BossAnim.DoScaledAnimationAsync("BossCall", 0.5f);
SoundByte.PlayOneShotGame(sfxName + "signal");
StartInterval(beat, beatInterval);
queuedInputs.Add(beat - intervalStartBeat);
}
public void PassTurn(double beat)
{
intervalStarted = false;
foreach (var input in queuedInputs)
else
{
BeatAction.New(instance, new List<BeatAction.Action>()
queuedIntervals.Add(new QueuedInterval()
{
new BeatAction.Action(input + beatInterval , delegate {
MeatToss Meat = Instantiate(MeatBase, gameObject.transform).GetComponent<MeatToss>();
Meat.startBeat = beat;
Meat.cueLength = beatInterval + input;
Meat.cueBased = false;
Meat.meatType = "LightMeat";
}),
beat = beat,
length = length,
autoPassTurn = autoPassTurn
});
}
queuedInputs.Clear();
}
public void StartInterval(double beat, float length, double gameSwitchBeat, bool autoPassTurn)
{
List<BeatAction.Action> actions = new() {
new(beat - 1, delegate { if (Conductor.instance.songPositionInBeatsAsDouble < beat) BossAnim.DoScaledAnimationFromBeatAsync("BossSignal", 0.5f, beat - 1); }),
};
var allCallEvents = GetRelevantMeatCallsBetweenBeat(beat, beat + length);
allCallEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
for (int i = 0; i < allCallEvents.Count; i++)
{
double eventBeat = allCallEvents[i].beat;
if (eventBeat >= gameSwitchBeat)
{
actions.Add(new BeatAction.Action(eventBeat, delegate
{
BossAnim.DoScaledAnimationAsync("BossCall", 0.5f);
SoundByte.PlayOneShotGame("meatGrinder/signal");
}));
}
}
BeatAction.New(this, actions);
if (autoPassTurn)
{
PassTurn(beat + length, beat, length, allCallEvents);
}
}
public static void QueueMeatToss(RiqEntity entity)
{
// queuedMeats.Add(entity);
}
public void MeatToss(double beat, bool bacon, int tackReaction, float tackReactionBeats, int bossReaction, float bossReactionBeats)
{
Meat meat = Instantiate(MeatBase, transform).GetComponent<Meat>();
meat.gameObject.SetActive(true);
meat.startBeat = beat;
meat.meatType = bacon ? Meat.MeatType.BaconBall : Meat.MeatType.DarkMeat;
meat.tackReaction = new Reaction(tackReaction, tackReactionBeats);
meat.bossReaction = new Reaction(bossReaction, bossReactionBeats);
// meat.reaction = reaction;
}
public static void PrePassTurn(double beat)
{
if (GameManager.instance.currentGame == "meatGrinder")
{
instance.PassTurnStandalone(beat);
}
else
{
passedTurns.Add(beat);
}
}
private static List<double> passedTurns = new();
private void PassTurnStandalone(double beat)
{
var lastInterval = EventCaller.GetAllInGameManagerList("meatGrinder", new string[] { "StartInterval" }).FindLast(x => x.beat <= beat);
if (lastInterval != null) PassTurn(beat, lastInterval.beat, lastInterval.length);
}
private void PassTurn(double beat, double intervalBeat, float intervalLength, List<RiqEntity> allCallEvents = null)
{
if (allCallEvents == null) {
allCallEvents = GetRelevantMeatCallsBetweenBeat(intervalBeat, intervalBeat + intervalLength);
allCallEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
}
List<BeatAction.Action> meatCalls = new();
for (int i = 0; i < allCallEvents.Count; i++)
{
double relativeBeat = allCallEvents[i].beat - intervalBeat;
var tackReaction = new Reaction(allCallEvents[i]["tackReaction"], allCallEvents[i]["tackReactionBeats"]);
var bossReaction = new Reaction(allCallEvents[i]["bossReaction"], allCallEvents[i]["bossReactionBeats"]);
meatCalls.Add(new BeatAction.Action(beat + relativeBeat - 1, delegate
{
Meat meat = Instantiate(MeatBase, transform).GetComponent<Meat>();
meat.gameObject.SetActive(true);
meat.startBeat = beat + relativeBeat - 1;
meat.meatType = Meat.MeatType.LightMeat;
meat.tackReaction = tackReaction;
meat.bossReaction = bossReaction;
}));
}
BeatAction.New(this, meatCalls);
}
}
}

View File

@ -1,73 +0,0 @@
using HeavenStudio.Util;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyBezierCurves;
namespace HeavenStudio.Games.Scripts_MeatGrinder
{
public class MeatToss : MonoBehaviour
{
public double startBeat;
public double cueLength;
public bool cueBased;
public string meatType;
[Header("Animators")]
private Animator anim;
private MeatGrinder game;
private void Awake()
{
game = MeatGrinder.instance;
anim = GetComponent<Animator>();
}
private void Start()
{
game.ScheduleInput(startBeat, cueLength, MeatGrinder.InputAction_Press, Hit, Miss, Nothing);
BeatAction.New(this, new List<BeatAction.Action>() {
new BeatAction.Action(cueBased ? startBeat + 0.66f : cueLength + startBeat - 1 + 0.66f, delegate {
anim.DoScaledAnimationAsync(meatType+"Thrown", 0.32f);
}),
});
}
private void Update()
{
if (anim.IsPlayingAnimationNames("DarkIdle") || anim.IsPlayingAnimationNames("LightIdle")) GameObject.Destroy(gameObject);
}
private void InputActions(bool annoyBoss, string whichSfx, string whichAnim)
{
game.bossAnnoyed = annoyBoss;
SoundByte.PlayOneShotGame("meatGrinder/"+whichSfx);
game.TackAnim.DoScaledAnimationAsync(whichAnim, 0.5f);
}
private void Hit(PlayerActionEvent caller, float state)
{
game.TackAnim.SetBool("tackMeated", false);
anim.DoScaledAnimationAsync(meatType+"Hit", 0.5f);
if (state >= 1f || state <= -1f) {
InputActions(true, "tink", "TackHitBarely");
} else {
InputActions(false, "meatHit", "TackHitSuccess");
}
}
private void Miss(PlayerActionEvent caller)
{
GameObject.Destroy(gameObject);
InputActions(true, "miss", "TackMiss"+meatType);
game.BossAnim.DoScaledAnimationAsync("BossMiss", 0.5f);
game.TackAnim.SetBool("tackMeated", true);
}
private void Nothing(PlayerActionEvent caller) { }
}
}

View File

@ -439,6 +439,13 @@ namespace HeavenStudio.Games
}
// added because OnBeatPulse had some animation issues going on
// if your bopping overlaps with other animations, use this instead
public virtual void OnLateBeatPulse(double beat)
{
}
public static MultiSound PlaySoundSequence(string game, string name, double startBeat, params SoundSequence.SequenceParams[] args)
{
Minigames.Minigame gameInfo = GameManager.instance.GetGameInfo(game);