Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using TheGuysYouDespise;
- public class HornBlock : BlockScript
- {
- //BlockSettings
- protected MKey activateKey;
- protected MSlider pitchSlider;
- protected MSlider volumeSlider;
- protected MToggle semitoneToggle;
- //other stuff
- protected AudioSource audioSource;
- protected bool hasSound = false;
- protected float HornsWithMyKeyCoefficient = 0f;
- protected bool flipped = false;
- protected Vector3 orgScale = Vector3.one;
- protected bool steamPowered = false;
- protected float steamPoweredTimer = 10f;
- protected float lastSoundTimer = 10f;
- //for the semitone toggle we'll be using we want to know a list of intervals we can use for that
- protected float[] musicIntervals = new float[] {1/1f, 16/15f, 9/8f, 6/5f, 5/4f, 4/3f, 64/45f, 3/2f, 8/5f, 5/3f, 16/9f, 15/8f, 2/1f};
- protected bool waitAFrame = true;
- //BlockLoader Specific method: is called right after the script is made - usually,
- //this is done for all blocks of this type and is safe as it waits for stuff like
- //colliders, visuals, resources and so on
- public override void SafeAwake()
- {
- //Set the blocks original scale
- orgScale = transform.localScale;
- activateKey = AddKey("Sound The Horn", //Display text
- "play", //save name
- KeyCode.H); //Keyboard button
- pitchSlider = AddSlider("Pitch", //Display Text
- "pitch", //save name
- 1f, //default value
- 0.5f, //minimum value
- 2f); //maximum value
- volumeSlider = AddSlider("Volume", //Display Text
- "volume", //save name
- 1f, //default value
- 0.1f, //minimum value
- 1f); //maximum value
- semitoneToggle = AddToggle("Semitone Snap", //Display Text
- "semitones", //save name
- true); //default value
- //Call HandleSemiToneSnap() when the slider value changes
- pitchSlider.ValueChanged += HandleSemitoneSnap;
- }
- //BlockLoader Specific method: Is the safe 1 time called method for the prefab - that's the master gameobject, or template so to speek
- //if you need to make alterations to the block you couldn't do with the standard framework, do it here.
- public override void OnPrefabCreation()
- {
- }
- //BlockLoader Specific method: When the player presses spacebar or the simulate/play button in the upper left corner
- protected override void OnSimulateStart()
- {
- //if the sound file we loaded in the LoadExampleBlock
- if (resources.ContainsKey("warHorn.ogg"))
- {
- hasSound = true;
- //set the audio source we'll be using to the respective component, but if we don't find one, add one.
- audioSource = gameObject.GetComponent<AudioSource>() ?? gameObject.AddComponent<AudioSource>();
- //set the clip the audio source will be playing to the one we loaded.
- audioSource.clip = resources["warHorn.ogg"].audioClip;
- }
- //This is mostly flair:
- //For all blocks in the machine we have during simulation
- foreach (Transform t in Machine.Active().SimulationMachine)
- {
- //get the ones that are a HornBlock
- if (t.GetComponent<HornBlock>())
- {
- //and if they use the save keyboard button to play their sound
- if (t.GetComponent<HornBlock>().activateKey.KeyCode[0] == activateKey.KeyCode[0])
- {
- //Add their volume to a coefficient we need for later
- HornsWithMyKeyCoefficient += t.GetComponent<HornBlock>().volumeSlider.Value;
- }
- }
- }
- }
- //BlockLoader Specific method: When the player is simulating instead of building
- protected override void OnSimulateUpdate()
- {
- //as long as our block isn't destroyed
- if (!HasBurnedOut() && !Destroyed())
- {
- //when we press the key
- if (activateKey.IsPressed)
- {
- //Play the horn sound
- SoundTheHorn();
- }
- //Or when steam powers the horn
- else if(steamPowered)
- {
- //We check all this stuff to stop spamming of powering the horn
- if (!audioSource.isPlaying || steamPoweredTimer > 0.1f || lastSoundTimer > 1f)
- {
- //Play the horn sound
- lastSoundTimer = 0f;
- SoundTheHorn();
- }
- }
- lastSoundTimer += Time.deltaTime;
- steamPoweredTimer += Time.deltaTime;
- steamPowered = false;
- }
- }
- //Checking Steam powering
- protected virtual void OnParticleCollision(GameObject other)
- {
- //ignore if we are already steam powered
- if (!steamPowered)
- {
- //if collison is with steam
- if (other.name == "SteamParticle")
- //if it's entering from the top-ish
- if (Vector3.Angle(-other.transform.forward, transform.forward) < 55f)
- {
- steamPoweredTimer = 0f;
- steamPowered = true;
- }
- }
- }
- //HornBlock defined method: We have made this method to play the sound we want
- public void SoundTheHorn()
- {
- StopCoroutine(ShakeScale(transform.FindChild("Vis").gameObject, 0.025f, 0.25f / pitchSlider.Value));
- StartCoroutine(ShakeScale(transform.FindChild("Vis").gameObject, 0.025f, 0.25f / pitchSlider.Value));
- if (hasSound)
- {
- //Set a couple of frames of random delay (we don't use Time.deltaTime, because if we have low fps we don't want to wait longer for the sounds to play)
- audioSource.PlayDelayed(Random.Range(0f, 0.0165f * 3f));
- //Set the pitch - the brightness - of the sound to be dependent on the timescale and the slider we have for it
- audioSource.pitch = Time.timeScale * pitchSlider.Value;
- //This is the flair coefficient, this basically helps the sounds to be dimmed when a lot of blocks play at the same time
- float Coefficient = 0.6f / (HornsWithMyKeyCoefficient < 4.5f ? 1f : HornsWithMyKeyCoefficient / 3f);
- //Set the volume - the loudness - of the sound to be dependent on the slider and coefficient
- audioSource.volume = volumeSlider.Value * Coefficient;
- }
- }
- //BlockLoader Specific method: When we are done simulating, usually you don't need to do anything here,
- //as the block in simulation mode is deleted, but if you have static variables or similar you might want to update it here.
- protected override void OnSimulateExit()
- {
- //reset this flair thing for good meassure
- HornsWithMyKeyCoefficient = 0f;
- }
- //Flip the block on F, this method needs to be called "Flip"
- public void Flip()
- {
- //play the flipping sound - BlockScript predefined
- PlayFlipSound();
- //Do the flipping we need:
- FlipNoSound();
- }
- //Setting the flip needed for copying, loading and such.
- public void SetFlipNoSound(bool flip)
- {
- //we don't want to do the flip if the flipping matches
- if (flipped == flip)
- return;
- //Do the flipping we need:
- FlipNoSound();
- }
- //HornBlock defined method: We flip the block litterally
- public void FlipNoSound()
- {
- //reverse the flipping bool
- flipped = !flipped;
- //mirror the visuals of the block to be... well... mirrored
- foreach(var vis in Visuals)
- MirrorVisual(vis); //BlockScript defined method
- //mirror the colliders in a different way where we make sure to rotate the colliders to achieve the correct stuff
- foreach (var col in Colliders)
- MirrorCollider(col); //BlockScript defined method
- }
- //HornBlock defined method:
- //Animates a shake in the scale of a gameobject
- protected IEnumerator ShakeScale(GameObject go, float magnitude, float duration)
- {
- float elapsed = Time.deltaTime;
- duration += Time.deltaTime;
- Vector3 orgScale = go.transform.localScale;
- Vector3 fromScale = orgScale;
- Vector3 targetScale = orgScale;
- float previousCoef = 10f;
- while (elapsed < duration)
- {
- elapsed += Time.deltaTime;
- //this if statement deals with time scale,
- //for >100% time scale it's run every frame
- //but as the time scale is lower we don't want the shaking to appear much faster than fitting for the time
- //in that scenario we will sample the shaking every now and then
- //we then smoothly lerp between the samples
- if (Time.timeScale >= 0.9f || (elapsed % 0.01666f) / 0.01666f < previousCoef)
- {
- go.transform.localScale = targetScale;
- //dampen based on the tails of the animation
- float percentComplete = elapsed / duration;
- float damper = 1.0f - Mathf.Clamp(5.0f * percentComplete - 4.0f, 0.0f, 1.0f);
- // map value to [-1, 1]
- float x = Random.value * 2.0f - 1.0f;
- float y = Random.value * 2.0f - 1.0f;
- // dampen
- x *= magnitude * damper;
- y *= magnitude * damper;
- //Set the two points to lerp between
- fromScale = go.transform.localScale;
- targetScale = new Vector3(orgScale.x + x, orgScale.y + y, orgScale.z);
- }
- previousCoef = (elapsed % 0.01666f) / 0.01666f;
- go.transform.localScale = Vector3.Lerp(fromScale, targetScale, (elapsed % 0.01666f) / 0.01666f);
- yield return null;
- }
- go.transform.localScale = orgScale;
- yield break;
- }
- //HornBlock defined method: make sure that the pitch only can fit a musical interval when toggled
- protected virtual void HandleSemitoneSnap(float value)
- {
- //we have to wait a frame not to create a feedback loop where when we change the value to snap to a semi tone it will then call this function again
- if (waitAFrame)
- {
- waitAFrame = false;
- float lower, upper;
- if (semitoneToggle.IsActive)
- {
- //we go through all of the semitones
- for (int i = 0; i < musicIntervals.Length - 1; i++)
- {
- lower = musicIntervals[i];
- upper = musicIntervals[i + 1];
- //for 2 octaves, below and above the
- for (int octave = 1; octave <= 2; octave++)
- {
- //we are inversing the intervals to get the intervals below 1.0 and above
- lower = 1f / lower;
- upper = 1f / upper;
- //see whether the value is between the lower and upper (lower and upper can be inverse, due to we inversing the fraction)
- if ((value >= lower && value <= upper)
- || (value >= upper && value <= lower))
- {
- //find the closer of the values
- if (Mathf.Abs(lower - value)
- < Mathf.Abs(upper - value))
- {
- pitchSlider.Value = lower;
- goto ReturnAndUpdate;
- }
- else
- {
- pitchSlider.Value = upper;
- goto ReturnAndUpdate;
- }
- }
- }
- }
- //we were outside the range of the intervals
- //clamp the value to the absolute lower and upper
- lower = 1f / musicIntervals[musicIntervals.Length - 1];
- upper = musicIntervals[musicIntervals.Length - 1];
- pitchSlider.Value = Mathf.Clamp(value, lower, upper);
- ReturnAndUpdate:
- StopAllCoroutines();
- StartCoroutine(UpdateMapper());
- }
- }
- else waitAFrame = true;
- }
- //HornBlock defined method
- protected virtual IEnumerator UpdateMapper()
- {
- if (BlockMapper.CurrentInstance == null)
- yield break;
- while (Input.GetMouseButton(0))
- yield return null;
- BlockMapper.CurrentInstance.Copy();
- BlockMapper.CurrentInstance.Paste();
- yield break;
- }
- //The following functions are for saving, loading, copying and pasting the values
- //Besiege Specific method
- public override void OnSave(BlockXDataHolder data)
- {
- SaveMapperValues(data);
- data.Write("flipped", flipped);
- }
- //Besiege Specific method
- public override void OnLoad(BlockXDataHolder data)
- {
- LoadMapperValues(data);
- // If the simulation just started we do not want to load
- // our flip state from the data holder.
- // The flip variable is automatically copied
- // over by Unity when the simulation starts.
- if (data.WasSimulationStarted) return;
- if (data.HasKey("flipped"))
- {
- SetFlipNoSound(data.ReadBool("flipped"));
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement