LadyAliceMargatroid f37cded831 Added more radio music, based on dungeon variant.
Redid vent code to work better and like work in general.
Knight animation now pauses for a second.
Knights no longer trigger the default scarlet vent animation.
Revenants now spawn at a vent if no knights exist.
2025-01-15 20:41:48 -08:00

234 lines
8.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Unity.Netcode;
using DunGen;
using ScarletMansion.Lights;
using ScarletMansion.GamePatch.Managers;
using GameNetcodeStuff;
using ScarletMansion.Configs;
namespace ScarletMansion.GamePatch.Components {
public class ScarletBedroom : MonoBehaviour, IDungeonCompleteReceiver {
public const string ENEMY_SPAWN_LIST_DEFAULT = "knight@s1,crawler,nutcracker,springman,maskedplayerenemy,jester@s1,butler";
public static readonly string[] ENEMY_EVIL_LIST = new string[] { "knight", "crawler", "nutcracker", "springman" ,"maskedplayerenemy" ,"jester", "butler" };
public static ActionList onBedroomEndEvent = new ActionList("onBedroomEnd");
[Header("References")]
public ScarletVentVisuals vent;
public Transform paintingSpawnTransform;
public AudioSource screamAudioSource;
public Transform[] itemSpawns;
private Light[] lights;
private Doorway[] doorways;
private ItemReference[] bonusItems;
private EnemyReferenceSpawnLogic bonusEnemy;
const float spawnTime = 10f;
const float cloudTime = 7f;
public void Anger(Transform target){
AngerManager.Instance.Anger();
StartCoroutine(PlayAudio());
StartCoroutine(IncreaseVentAudio());
StartCoroutine(LockDoorAndSpawnEnemy(target));
foreach(var l in lights){
StartCoroutine(FlickerLight(l));
}
}
public IEnumerator PlayAudio(){
yield return new WaitForSeconds(UnityEngine.Random.Range(0.75f, 1f));
screamAudioSource.Play();
AngerManager.Instance.TriggerAngerLightBrief(6f);
var local = StartOfRound.Instance.localPlayerController;
var dist = Vector3.SqrMagnitude(local.transform.position - transform.position);
if (dist < 12f * 12f)
local.JumpToFearLevel(0.75f);
else if (dist < 24f * 24f)
local.JumpToFearLevel(0.5f);
else if (dist < 32f * 32f)
local.JumpToFearLevel(0.25f);
}
public IEnumerator LockDoorAndSpawnEnemy(Transform target){
var roundmanager = RoundManager.Instance;
var doors = new List<ScarletDoor>();
// lock doors
if (roundmanager.IsServer){
foreach(var d in doorways){
var result = ScarletGenericManager.Instance.GetScarletDoor(d.transform.position);
if (result) doors.Add(result);
}
foreach(var d in doors){
d.LockDoorClientRpc();
}
}
yield return new WaitForSeconds(spawnTime);
SpawnRandomEnemy(target);
SpawnLoot();
onBedroomEndEvent.Call();
// stop volume stuff
vent.StopSpawnParticles();
vent.StopLightningParticles();
ScarletNetworkManager.Instance.CreateSpawnAudioPrefabLocal(target.transform.position);
// unlock doors
if (roundmanager.IsServer){
foreach(var d in doors){
d.UnlockDoorClientRpc();
}
}
}
public IEnumerator IncreaseVentAudio(){
yield return new WaitForSeconds(3f);
vent.StartSpawnParticles();
var t = 0f;
while (t < cloudTime){
t += Time.deltaTime;
vent.SetVolumeLazy(t / cloudTime);
yield return null;
}
}
public IEnumerator FlickerLight(Light light){
// gives time for player to react to their environment
yield return new WaitForSeconds(UnityEngine.Random.Range(1f, 1.5f));
// scarlet lights flicker
var scarletLight = light.GetComponent<ScarletLight>();
if (scarletLight != null){
scarletLight.SetFlickeringState(ScarletLight.FlickeringState.Heavy);
}
// normal lights are candles, so just turn them off
else {
yield return new WaitForSeconds(UnityEngine.Random.Range(3f, 5f));
light.enabled = false;
}
}
private static List<EnemyReferenceSpawnLogic> spawnableEnemiesTrueList;
public static void CreateRandomEnemyList(List<SpawnableEnemyWithRarity> enemies){
var lower = ConfigMain.Instance.paintingEnemyListValue.ToLowerInvariant();
if (lower == "default"){
lower = ENEMY_SPAWN_LIST_DEFAULT;
}
spawnableEnemiesTrueList = new List<EnemyReferenceSpawnLogic>();
var entries = Utility.ParseString(lower, new string[] {","}, new string[] {"@"} );
for(var i = 0; i < enemies.Count; ++i){
var enemyType = enemies[i].enemyType;
var enemyName = enemyType.name.ToLowerInvariant();
var enemyDisplayName = enemyType.enemyName.ToLowerInvariant();
var link = entries.FirstOrDefault(e => e.main == enemyName || e.main == enemyDisplayName);
if (link != null){
EnemyReferenceSpawnLogic.SpawnLogic spawnValue;
var spawnTag = link.GetParameter("s");
if (spawnTag != null) spawnValue = (EnemyReferenceSpawnLogic.SpawnLogic)Utility.TryParseInt(spawnTag.Substring(1), 0);
else spawnValue = 0;
var reference = new EnemyReferenceSpawnLogic(enemyType, i, spawnValue);
spawnableEnemiesTrueList.Add(reference);
Plugin.logger.LogDebug($"Added {reference.ToString()} to bedroom event");
}
}
}
public EnemyReferenceSpawnLogic GetRandomEnemy(System.Random sysRandom){
var roundManager = RoundManager.Instance;
if (!roundManager.IsServer) return null;
if (!ConfigMain.Instance.paintingSpawnEnemyValue) return null;
if (spawnableEnemiesTrueList == null || spawnableEnemiesTrueList.Count == 0){
Plugin.logger.LogError($"Could not select enemy to spawn in bedroom. Empty enemy list?");
return null;
}
var index = sysRandom.Next(spawnableEnemiesTrueList.Count);
var enemy = spawnableEnemiesTrueList[index];
Plugin.logger.LogDebug($"Selected enemy to spawn {enemy.ToString()}");
return enemy;
}
public void SpawnRandomEnemy(Transform target, int overrideIndex = -1){
var roundmanager = RoundManager.Instance;
if (!roundmanager.IsHost) return;
if (bonusEnemy == null) return;
try {
var enemy = bonusEnemy.enemy;
if (bonusEnemy.index > -1){
var pos = vent.transform.position;
var dir = target.transform.position - pos;
dir.y = 0f;
var rot = Quaternion.LookRotation(dir);
var y = rot.eulerAngles.y;
bonusEnemy.ApplySpawnLogic();
roundmanager.currentEnemyPower += enemy.PowerLevel;
var spawnedEnemy = ScarletNetworkManagerUtility.CreateEnemyWithRef(bonusEnemy, pos, y);
ScarletNetworkManager.Instance.RequestEvilSkinApply(spawnedEnemy, enemy.name.ToLowerInvariant());
}
} catch (Exception e) {
var enemyString = bonusEnemy != null ? bonusEnemy.ToString() : "NULL";
Plugin.logger.LogError($"Failed to spawn enemy for bedroom event, the smelly culprit is {enemyString}");
Plugin.logger.LogError(e.ToString());
}
}
public void SpawnLoot(){
var roundmanager = RoundManager.Instance;
if (!roundmanager.IsServer) return;
try {
AngerManager.SpawnAngerLoot(bonusItems, itemSpawns);
} catch (Exception e) {
var itemStrings = string.Join("\n", bonusItems.Select(i => i != null ? i.ToString() : "NULL"));
Plugin.logger.LogError($"Failed to spawn items for bedroom event, the smelly culprits are {itemStrings}");
Plugin.logger.LogError(e.ToString());
}
}
public void OnDungeonComplete(Dungeon dungeon) {
ScarletGenericManager.Instance.AddBedroom(this);
ScarletGenericManager.Instance.AddRoomOfInterest(transform);
var parent = GetComponentInParent<Tile>();
lights = parent.GetComponentsInChildren<Light>();
doorways = parent.UsedDoorways.ToArray();
var sysRandom = DunGenPatch.Patch.CreateSystemRandom();
Utility.Shuffle(sysRandom, itemSpawns);
var count = sysRandom.Next(Plugin.CurrentConfigDungeon.GetPaintingExtraLoot.min, Plugin.CurrentConfigDungeon.GetPaintingExtraLoot.max + 1);
bonusItems = AngerManager.CreateAngerLoot(count, sysRandom);
bonusEnemy = GetRandomEnemy(sysRandom);
}
}
}