Moved dungen code to DunGenPlus

Added critical damage as a mechanic
Maid knife feed deals critical damage, then kills on second time
Added concept of treasure room with kitchen
Frames now only start at the player when not being looked
New attempt at snowglobe animations, it broke though
Added concept of indoor weathers
LethalConfig compability now changes the color of configs that my presets touch
Added jukebox
Changed modGUID
Removed AC compability
Falling into void deals critical damage and teleports, then kills on second time
Maid spawns revenant ghost on death
This commit is contained in:
LadyAliceMargatroid 2024-08-02 08:56:09 -07:00
parent faa391c309
commit 056cac8df1
63 changed files with 976 additions and 2061 deletions

View file

@ -0,0 +1,60 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components
{
public class PathOpenUpParent : MonoBehaviour, IDungeonCompleteReceiver {
[System.Serializable]
public class Chokepoint {
public PathOpenup[] paths;
public void UpdatePath(System.Random sysRandom){
var count = sysRandom.Next(1, Mathf.Min(paths.Length, 2));
var length = paths.Length;
var items = new int[length];
for(var i = 0; i < length; i++){
items[i] = i;
}
Utility.Shuffle(sysRandom, items);
var j = 0;
while(j < 1) {
paths[j].UpdatePath(PathOpenup.State.Active);
++j;
}
while(j < length) {
paths[j].UpdatePath(PathOpenup.State.UnActive);
++j;
}
}
}
public Chokepoint[] chokepoints;
public PathOpenup[] paths;
void Reset(){
paths = GetComponentsInChildren<PathOpenup>();
}
public void OnDungeonComplete(Dungeon dungeon) {
var anyChanges = true;
var dunRandom = DunGenPatch.Patch.generatorInstance.RandomStream;
var sysRandom = new System.Random(dunRandom.Next());
foreach(var c in chokepoints) {
c.UpdatePath(sysRandom);
}
foreach(var c in paths) {
c.CleanUp();
}
}
}
}

View file

@ -0,0 +1,40 @@
using DunGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components {
public class PathOpenup : MonoBehaviour {
public GameObject[] connectorGameObjects;
public GameObject[] blockerGameObjects;
public enum State {
NotSet,
UnActive,
Active
}
private State state;
public void UpdatePath(State newState){
if (newState < state) return;
var connect = newState == State.Active;
foreach(var connectorGameObject in connectorGameObjects) connectorGameObject.SetActive(connect);
foreach(var blockerGameObject in blockerGameObjects) blockerGameObject.SetActive(!connect);
state = newState;
}
public void CleanUp() {
var targets = state == State.Active ? blockerGameObjects : connectorGameObjects;
foreach(var t in targets) Destroy(t);
}
}
}

View file

@ -182,11 +182,9 @@ namespace ScarletMansion.GamePatch.Components {
if (bonusEnemy == null) return;
try {
var enemy = bonusEnemy.enemy;
var enemyIndex = bonusEnemy.index;
if (enemyIndex > -1){
if (bonusEnemy.index > -1){
var pos = vent.transform.position;
var dir = target.transform.position - pos;
dir.y = 0f;
@ -196,7 +194,7 @@ namespace ScarletMansion.GamePatch.Components {
bonusEnemy.ApplySpawnLogic();
roundmanager.currentEnemyPower += enemy.PowerLevel;
var spawnedEnemy = roundmanager.SpawnEnemyGameObject(vent.transform.position, y, enemyIndex);
var spawnedEnemy = ScarletNetworkManagerUtility.CreateEnemyWithRef(bonusEnemy, vent.transform.position, y);
ScarletNetworkManager.Instance.RequestEvilSkinApply(spawnedEnemy, enemy.name.ToLowerInvariant());
}
@ -223,6 +221,7 @@ namespace ScarletMansion.GamePatch.Components {
public void OnDungeonComplete(Dungeon dungeon) {
AngerManager.Instance.AddBedroom(this);
AngerManager.Instance.AddRoomOfInterest(transform);
var parent = GetComponentInParent<Tile>();
lights = parent.GetComponentsInChildren<Light>();

View file

@ -39,10 +39,10 @@ namespace ScarletMansion.GamePatch.Components {
}
}
var timeOfDay = TimeOfDay.Instance;
var totalMinutes = (int)(timeOfDay.normalizedTimeOfDay * (60f * timeOfDay.numberOfHours)) + 360;
var hours = Mathf.FloorToInt(totalMinutes / 60f);
var minutes = totalMinutes % 60;
var time = Utility.GetTime();
var totalMinutes = time.totalMinutes;
var hours = time.hours;
var minutes = time.minutes;
var timeChanged = lastTotalMinutes != totalMinutes;
var hourChanged = lastHour != hours;

View file

@ -13,6 +13,9 @@ namespace ScarletMansion.GamePatch.Components {
[Header("Door Reference")]
public DoorLock door;
public bool doorState;
public bool overrideLock;
public bool overrideUnlock;
[Header("Personal Refs")]
public NavMeshObstacle obstacle;
@ -25,18 +28,40 @@ namespace ScarletMansion.GamePatch.Components {
AngerManager.Instance.AddDoor(this);
}
[ServerRpc]
public void LockDoorServerRpc(){
LockDoorClientRpc();
}
[ServerRpc]
public void UnlockDoorServerRpc(){
UnlockDoorClientRpc();
[ClientRpc]
public void LockDoorClientRpc(){
doorState = true;
ReevalulateDoorState();
}
[ClientRpc]
public void LockDoorClientRpc(){
public void UnlockDoorClientRpc(){
doorState = false;
ReevalulateDoorState();
}
[ClientRpc]
public void LockDoorOverrideClientRpc(bool state){
overrideLock = state;
ReevalulateDoorState();
}
[ClientRpc]
public void UnlockDoorOverrideClientRpc(bool state){
overrideUnlock = state;
ReevalulateDoorState();
}
private void ReevalulateDoorState(){
var state = doorState;
if (overrideUnlock) state = false;
if (overrideLock) state = true;
if (state) LockDoorCall();
else UnlockDoorCall();
}
private void LockDoorCall(){
if (door){
door.isDoorOpened = false;
door.navMeshObstacle.enabled = true;
@ -54,8 +79,7 @@ namespace ScarletMansion.GamePatch.Components {
ps.Play();
}
[ClientRpc]
public void UnlockDoorClientRpc(){
private void UnlockDoorCall() {
if (door){
door.doorTrigger.interactable = true;
door.isLocked = previousDoorLockValue;

View file

@ -6,7 +6,9 @@ using System.Threading.Tasks;
using UnityEngine;
using Unity.Netcode;
using ScarletMansion.GamePatch.Enemies;
using System.Security.Permissions;
[assembly: SecurityPermission( SecurityAction.RequestMinimum, SkipVerification = true )]
namespace ScarletMansion.GamePatch.Components {
public class ScarletDoorLock : DoorLock {
@ -86,7 +88,8 @@ namespace ScarletMansion.GamePatch.Components {
{ typeof(SpringManAI), (e) => e.currentBehaviourStateIndex == 0 },
{ typeof(KnightVariant), (e) => e.currentBehaviourStateIndex == 0 },
{ typeof(ButlerEnemyAI), (e) => e.currentBehaviourStateIndex <= 1 },
{ typeof(MaidVariant), (e) => e.currentBehaviourStateIndex <= 1 }
{ typeof(MaidVariant), (e) => e.currentBehaviourStateIndex <= 1 },
{ typeof(KnightGhostVariant), (e) => false }
};
static readonly Dictionary<Type, float> EnemyDoorDamagePerSecond = new Dictionary<Type, float>(){
@ -104,7 +107,8 @@ namespace ScarletMansion.GamePatch.Components {
{ typeof(SpringManAI), 12.5f },
{ typeof(KnightVariant), 12.5f },
{ typeof(ButlerEnemyAI), 50f },
{ typeof(MaidVariant), 50f }
{ typeof(MaidVariant), 50f },
{ typeof(KnightGhostVariant), 9999f }
};
public bool IsInOpenDoorNormallyState(EnemyAI enemy){

View file

@ -8,42 +8,31 @@ using Unity.Netcode;
using GameNetcodeStuff;
namespace ScarletMansion.GamePatch.Components {
public class ScarletFrame : NetworkBehaviour {
public class ScarletFrame : MonoBehaviour {
//public MeshRenderer renderer;
//public Material[] materials;
public float visualUpdate = 0.2f;
private float visualUpdateCurrent = 0f;
public void OnInteract(PlayerControllerB player){
var direction = player.transform.position - transform.position;
direction.y = 0f;
ChangeDirection(direction);
ChangeDirectionServerRpc(direction);
void Start(){
visualUpdateCurrent = UnityEngine.Random.value * visualUpdate;
}
/*
[ClientRpc]
public void UpdateMaterialClientRpc(int value){
var mats = new List<Material>(2);
renderer.GetMaterials(mats);
mats[1] = materials[value % materials.Length];
renderer.SetMaterials(mats);
}
*/
[ServerRpc(RequireOwnership = false)]
public void ChangeDirectionServerRpc(Vector3 direction){
ChangeDirectionClientRpc(direction);
void Update() {
visualUpdateCurrent += Time.deltaTime;
if (visualUpdateCurrent > visualUpdate) {
LookAtLocalPlayer();
visualUpdateCurrent = 0f;
}
}
[ClientRpc]
public void ChangeDirectionClientRpc(Vector3 direction){
ChangeDirection(direction);
}
void LookAtLocalPlayer(){
var localPlayer = StartOfRound.Instance.localPlayerController;
if (localPlayer && !localPlayer.isPlayerDead) {
var direction = localPlayer.transform.position - transform.position;
direction.y = 0f;
public void ChangeDirection(Vector3 direction){
transform.rotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.LookRotation(direction);
}
}
}

View file

@ -9,12 +9,45 @@ using GameNetcodeStuff;
namespace ScarletMansion.GamePatch.Components {
public class ScarletPlayerControllerB : MonoBehaviour {
public static Dictionary<PlayerControllerB, ScarletPlayerControllerB> playerControllers;
// self
public PlayerControllerB player;
// animation
public AnimatorOverrideController playerOverrideController;
// second-chance states
public bool stabbedSelf;
public bool fellInPit;
public static ScarletPlayerControllerB GetScarletPlayerScript(PlayerControllerB player) {
if (!playerControllers.TryGetValue(player, out var scarlet)) {
scarlet = player.GetComponent<ScarletPlayerControllerB>();
if (scarlet == null) {
Plugin.logger.LogError($"Couldn't find scarlet player script for {player}. Kinda bad");
return null;
}
Plugin.logger.LogMessage($"Scarlet player script for {player} was not initially registered. We good now but like why?");
playerControllers.Add(player, scarlet);
}
return scarlet;
}
public static void InitializeScarletScripts() {
playerControllers = new Dictionary<PlayerControllerB, ScarletPlayerControllerB>();
}
public void Initialize(PlayerControllerB player){
this.player = player;
CreateHelmetForFlashlight(player, Assets.flashlight, 0);
CreateHelmetForFlashlight(player, Assets.flashlightBB, 1);
CreateHelmetForFlashlight(player, Assets.flashlightBB, 1);
}
public void Register(){
if (!playerControllers.ContainsKey(player))
playerControllers.Add(player, this);
}
public static void CreateHelmetForFlashlight(PlayerControllerB player, Assets.Flashlight flashlight, int index){

View file

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components {
public class ScarletRadio : NetworkBehaviour{
[Header("Networked Values")]
public int volume;
public bool playing;
public int audioIndex;
[Header("Songs")]
public AudioClip[] audioClips;
[Header("References")]
public AudioSource audioSource;
public float maxVolume = 1f;
public Transform onOffSwitchTransform;
public Transform volumeSwitchTransform;
public Transform volumeDialTransform;
public Transform tuneSwitchTransform;
private float onOffSwitchTransformX;
private float volumeSwitchTransformX;
private float volumeDialTransformX;
private float onOffSwitchTransforX;
[Header("Switch Values")]
public Vector2 onOffSwitchRotation;
public Vector2 volumeSwitchRotation;
public Vector2 volumeDialRotation;
public Vector2 tuneSwitchRotation;
void Start() {
audioSource.volume = maxVolume * (volume * 0.1f);
}
void RotateTransformTo(Transform dial, ref float current, float towards){
current = Mathf.Lerp(current, towards, Time.deltaTime * 2f);
var v = new Vector3(current, 0f, 0f);
dial.localEulerAngles = v;
}
void RotateTransformTo(Transform dial, ref float current, Vector2 lerpVector, float lerp){
RotateTransformTo(dial, ref current, Mathf.Lerp(lerpVector.x, lerpVector.y, lerp));
}
void Update(){
RotateTransformTo(onOffSwitchTransform, ref onOffSwitchTransformX, playing ? onOffSwitchRotation.y : onOffSwitchRotation.x);
RotateTransformTo(volumeSwitchTransform, ref volumeSwitchTransformX, volumeSwitchRotation, volume * 0.1f);
RotateTransformTo(volumeDialTransform, ref volumeDialTransformX, volumeDialRotation, volume * 0.1f);
RotateTransformTo(tuneSwitchTransform, ref onOffSwitchTransforX, tuneSwitchRotation, (float)audioIndex / (audioClips.Length - 1));
}
public void ToggleOnOffSwitch(){
ToggleOnOffSwitchServerRpc();
}
public void ToggleVolumeSwitchLeft() {
ToggleVolumeSwitchServerRpc(-1);
}
public void ToggleVolumeSwitchRight() {
ToggleVolumeSwitchServerRpc(1);
}
public void ToggleSongSwitch(){
ToggleSongSwitchServerRpc();
}
[ServerRpc(RequireOwnership = false)]
public void ToggleOnOffSwitchServerRpc(){
ToggleOnOffSwitchClientRpc(!playing);
}
[ClientRpc]
public void ToggleOnOffSwitchClientRpc(bool playing){
this.playing = playing;
if (playing) AttemptTurnOn();
else AttemptTurnOff();
}
[ServerRpc(RequireOwnership = false)]
public void ToggleVolumeSwitchServerRpc(int count){
ToggleVolumeSwitchClientRpc(Mathf.Clamp(volume + count, 0, 10));
}
[ClientRpc]
public void ToggleVolumeSwitchClientRpc(int volume){
this.volume = volume;
audioSource.volume = maxVolume * (volume * 0.1f);
}
[ServerRpc(RequireOwnership = false)]
public void ToggleSongSwitchServerRpc(){
ToggleSongSwitchClientRpc((audioIndex + 1) % audioClips.Length);
}
[ClientRpc]
public void ToggleSongSwitchClientRpc(int audioIndex){
this.audioIndex = audioIndex;
if (playing && audioSource.isPlaying) audioSource.Stop();
audioSource.clip = audioClips[audioIndex];
AttemptTurnOn();
}
public void AttemptTurnOff(){
if (!playing && audioSource.isPlaying) audioSource.Stop();
}
public void AttemptTurnOn(){
if (playing && !audioSource.isPlaying) audioSource.Play();
}
}
}

View file

@ -29,7 +29,27 @@ namespace ScarletMansion.GamePatch.Components
if (audioClipIndex == -1) comp.statusEffectAudioIndex = 0;
comp.statusEffectAudioIndex = audioClipIndex;
if (comp.isSinking) return;
if (comp.isSinking) {
// teleporting them out for a second time in life
// won't be easy though kek
var scarletPlayer = ScarletPlayerControllerB.GetScarletPlayerScript(comp);
if (scarletPlayer != null && !scarletPlayer.fellInPit && comp.sinkingValue >= 0.9f && comp.health > 20) {
var selfPos = scarletPlayer.transform.position;
var farthestAINode = RoundManager.Instance.insideAINodes
.Select(n => n.transform.position)
.OrderByDescending(n => (selfPos - n).magnitude).FirstOrDefault();
comp.TeleportPlayer(farthestAINode);
var damage = ScarletNetworkManagerUtility.GetCriticalDamageToPlayer(comp, false);
comp.DamagePlayer(damage, false, true, CauseOfDeath.Suffocation);
ScarletNetworkManager.Instance.CreateSpawnAudioPrefab(farthestAINode, comp.actualClientId);
StopSinkingLocalPlayer(comp);
scarletPlayer.fellInPit = true;
}
return;
}
if (sinkingLocalPlayer){
if (!comp.CheckConditionsForSinkingInQuicksand()) {

View file

@ -0,0 +1,74 @@
using DunGen;
using ScarletMansion.GamePatch.Managers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
namespace ScarletMansion.GamePatch.Components.TreasureRoom {
public class TreasureRoomTimedOpen : MonoBehaviour, IDungeonCompleteReceiver {
[Header("References")]
public GameObject timeGameObject;
public TextMeshPro timeTextMesh;
[Header("Values")]
public Vector2Int hourRange;
public bool opened;
private int hourSelected;
private int mintuesSelected;
private ScarletDoor treasureDoorLock;
public void Update(){
if (treasureDoorLock == null) return;
if (!StartOfRound.Instance.IsHost || opened) return;
var time = Utility.GetTime();
if (time.hours > hourSelected || (time.hours >= hourSelected && time.minutes >= mintuesSelected)) {
treasureDoorLock.LockDoorOverrideClientRpc(false);
opened = true;
Plugin.logger.LogInfo($"Opening cause {time.hours}:{time.minutes} > {hourSelected}:{mintuesSelected}");
}
}
public void OnDungeonComplete(Dungeon dungeon) {
AngerManager.Instance.AddRoomOfInterest(transform);
if (!StartOfRound.Instance.IsHost) return;
var tile = GetComponentInParent<Tile>();
var doorways = tile.UsedDoorways;
foreach(var d in doorways) {
var neighboorTile = d.ConnectedDoorway.Tile.gameObject.name.ToLowerInvariant();
//Plugin.logger.LogInfo(neighboorTile);
if (neighboorTile.Contains("treasure")){
StartCoroutine(BeginTreasureRoomProcess(d));
return;
}
}
}
public IEnumerator BeginTreasureRoomProcess(Doorway doorway){
yield return new WaitForSecondsRealtime(4f);
Plugin.logger.LogInfo("Setting up lock for treasure room");
treasureDoorLock = AngerManager.Instance.GetScarletDoor(doorway.transform.position);
treasureDoorLock.LockDoorOverrideClientRpc(true);
hourSelected = UnityEngine.Random.Range(hourRange.x, hourRange.y);
mintuesSelected = UnityEngine.Random.Range(0, 3) * 15;
timeGameObject.SetActive(true);
timeTextMesh.text = $"{hourSelected}:{mintuesSelected}";
Plugin.logger.LogInfo($"Opening at {hourSelected}:{mintuesSelected}");
}
}
}