Types:
Language:
Year:
Notice: All games are only available on PC.
using System;
using System.Collections.Generic;
public static class RandomGen
{
public static int Seed { get; private set; }
private const string DEFAULT = "default";
private static Dictionary<string, System.Random> randomGenerators = new();
private struct RandomInfo
{
public int StartingSeed;
public RandomInfo(int startingSeed)
{
StartingSeed = startingSeed;
}
}
public static void Initialize()
{
Seed = (int)(DateTime.Now.Ticks & 0xFFFFFFF);
randomGenerators.Clear();
}
public static void Initialize(int seed)
{
Seed = seed;
randomGenerators.Clear();
}
// key would be for example battle or roomGeneration
public static int Get(string key)
{
if(!randomGenerators.ContainsKey(key))
{
int keySeed = Seed + key.GetHashCode();
randomGenerators[key] = new System.Random(keySeed);
}
return randomGenerators[key].Next();
}
public static int Get() => Get(DEFAULT);
}
// Adjustable Variables
[Header("AI Settings")]
public Monsters _monster;
public int _navAgentTypeID;
public IdleLogic _idleLogic;
public DoorLogic _doorLogic;
public InteractionsLogic _interactionsLogic;
public PatrolLogic _patrolLogic;
public ChaseLogic _chaseLogic;
[ShowIf(nameof(CheckForMolerat))] [AllowNesting] public VentLogic _ventLogic;
[ShowIf(nameof(CheckForFunnyGuy))] [AllowNesting] public StalkLogic _stalkLogic;
[Header("Interaction Scripts")]
public MonsterComebackTrigger _comebackTrigger;
public ThrowableLogic _throwableLogic;
public DialogueBank _afterFirstEncounter;
[ShowIf(nameof(CheckForFunnyGuy))] public TeleportThrowable _teleportThrowableLogic;
[Header("Kill Logics")]
public FunnyGuyKillLogic _funnyGuyKillLogic;
[Header("General Settings")]
public float _baseSpeed = 12f;
public GameObject _model;
public Animator animator;
public Transform _fallbackTransform;
[ShowIf(nameof(CheckForFunnyGuy))] [AllowNesting] public float _pissOffCap = 100f;
void Awake()
{
monsterPathing = GetComponent<NavMeshAgent>();
_iIdleLogic = SelectIdle.Idle(_idleLogic, this);
_iVentLogic = SelectVent.Vent(_ventLogic, this);
_iPatrolLogic = SelectPatrol.Patrol(_patrolLogic, this);
_iStalkLogic = SelectStalk.Stalk(_stalkLogic, this);
_iChaseLogic = SelectChase.Chase(_chaseLogic, this);
_iDoorLogic = SelectDoor.Door(_doorLogic, this);
_iInteractionsLogic = SelectInteractions.Interactions(_interactionsLogic, this);
col = GetComponent<BoxCollider>();
}
void Update()
{
if(_doNothing || _comeback) return;
if (IsPlayerLookingAtMonster() && !_playerSeenMonster)
{
seenTimer += Time.deltaTime;
if (seenTimer >= 0.5f)
{
_playerSeenMonster = true;
}
}
switch(monsterMode)
{
case MonsterMode.Idle:
_iIdleLogic?.HandleIdleMode();
break;
case MonsterMode.Vent:
_iVentLogic?.HandleVentMode();
if(_iVentLogic == null) goto default;
break;
case MonsterMode.Door:
_iDoorLogic?.HandleDoorMode();
if(_iDoorLogic == null) goto default;
break;
case MonsterMode.Patrol:
_iPatrolLogic?.HandlePatrolMode();
if(_iPatrolLogic == null) goto default;
break;
case MonsterMode.Chase:
_iChaseLogic?.HandleChaseMode();
if(_iChaseLogic == null) goto default;
break;
case MonsterMode.Stalk:
_iStalkLogic?.HandleStalkMode();
if(_iStalkLogic == null) goto default;
break;
case MonsterMode.Interactions:
_iInteractionsLogic?.HandleInteractionsMode();
if(_iInteractionsLogic == null) goto default;
break;
case MonsterMode.Nothing:
break;
default:
Debug.LogError($"Unknown monster mode, got switched to {monsterMode.ToString()}, going to Idle");
ChangeMonsterState(MonsterMode.Idle);
break;
}
}
void Update()
{
if(pickupCooldown > 0f) pickupCooldown -= Time.deltaTime;
// Manual hand velocity tracking for reliable VR throws
velocity = ((transform.position - lastPosition) / Time.fixedDeltaTime) * 1f;
Quaternion deltaRotation = transform.rotation * Quaternion.Inverse(lastRotation);
deltaRotation.ToAngleAxis(out float angle, out Vector3 axis);
if (angle > 180f) angle -= 360f;
angularVelocity = (axis * angle * Mathf.Deg2Rad / Time.fixedDeltaTime) * 1f;
lastPosition = transform.position;
lastRotation = transform.rotation;
}
public void HoldObject(GameObject item)
{
pickupCooldown = 0.15f;
_pickup = item.GetComponent<Pickup>();
_item = _pickup._item;
heldObject = item;
if(item.TryGetComponent<Rigidbody>(out Rigidbody rb))
{
heldRB = rb;
rb.isKinematic = true;
rb.useGravity = false;
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
ChildColliders(item, false);
item.transform.SetParent(transform.parent, true);
item.transform.localPosition = Vector3.zero;
item.transform.localRotation = Quaternion.identity;
}
public void DropObject(bool pocket)
{
if (heldObject == null) return;
Transform obj = heldObject.transform;
Rigidbody rb = heldRB;
obj.SetParent(null, true);
ChildColliders(heldObject, true);
if (rb != null)
{
rb.isKinematic = false;
rb.useGravity = true;
// Clamp to prevent insane physics launch on release
rb.linearVelocity = Vector3.ClampMagnitude(velocity, 24f);
rb.angularVelocity = Vector3.ClampMagnitude(angularVelocity, 45f);
rb.linearVelocity += transform.forward * 0.5f;
rb.WakeUp();
}
if(pocket) _pickup.PocketInInventory();
Reset();
}
public Sound PlaySoundHandle(string eventName, GameObject pos, Dictionary<string, float> parameters = null)
{
string fullPath = "event:/Sounds/" + eventName;
FMOD.Studio.EventInstance instance = RuntimeManager.CreateInstance(fullPath);
Globals.SpatialSounds(instance, pos);
if(parameters != null)
{
foreach(var param in parameters)
{
instance.setParameterByName(param.Key, param.Value);
}
}
instance.start();
instance.release();
return new Sound(instance);
}
public void StopAll()
{
SFXBus.stopAllEvents(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
VoicesBus.stopAllEvents(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
MusicBus.stopAllEvents(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
}
public void Mute()
{
isMuted = true;
SFXBus.setMute(true);
VoicesBus.setMute(true);
MusicBus.setMute(true);
}
public void Unmute()
{
isMuted = false;
SFXBus.setMute(false);
VoicesBus.setMute(false);
MusicBus.setMute(false);
}
// Oxygen.cs — scales tank capacity and depletion rate
switch(Globals.DifficultyMode)
{
case Difficulty.Easy:
actualOxygenCap = _oxygenCap * Globals.Plus50Percent;
_depletionMultiplier = Globals.Minus50Percent;
_capacityDepletion = Globals.Minus50Percent;
break;
case Difficulty.Normal:
actualOxygenCap = _oxygenCap * 1.0f;
_depletionMultiplier = 1.0f;
_capacityDepletion = 1.0f;
break;
case Difficulty.Hard:
actualOxygenCap = _oxygenCap * Globals.Minus50Percent;
_depletionMultiplier = Globals.Plus50Percent;
_capacityDepletion = Globals.Plus50Percent;
break;
case Difficulty.Permadeath:
actualOxygenCap = _oxygenCap * Globals.Minus75Percent;
_depletionMultiplier = Globals.Plus100Percent;
_capacityDepletion = Globals.Plus50Percent;
break;
}
// GainOxygen.cs — scales how fast oxygen refills at stations
switch(Globals.DifficultyMode)
{
case Difficulty.Easy:
oxygenGainSpeedMultiplier *= Globals.Minus50Percent;
break;
case Difficulty.Normal:
oxygenGainSpeedMultiplier = 1f;
break;
case Difficulty.Hard:
oxygenGainSpeedMultiplier *= Globals.Plus50Percent;
break;
case Difficulty.Permadeath:
oxygenGainSpeedMultiplier *= Globals.Plus75Percent;
break;
}
void OnEnable()
{
if(GameManager.Instance == null) return;
if(!GameManager.Instance.ShopGenerated)
{
GameManager.Instance.ShopGenerated = true;
_items = new GameObject[4];
List<int> usedIndices = new List<int>();
int random;
System.Random seededRandom = new System.Random(RandomGen.Seed);
for(int i = 0; i < _items.Length; i++)
{
do
{
random = seededRandom.Next(0, _availableItems.Length);
}
while (usedIndices.Contains(random));
GameObject newItem = Instantiate(_itemUIPrefab, _buttonContainer);
_items[i] = newItem;
Buy buyObject = _items[i].GetComponent<Buy>();
buyObject._item = _availableItems[random];
TMP_Text itemText = _items[i].GetComponentInChildren<TMP_Text>();
itemText.text = $"{_availableItems[random].itemName}\n\n\n\n\n\n\n\n${_availableItems[random].price}";
usedIndices.Add(random);
}
}
}
private void PlayJingle(bool PAMode)
{
(paJinglePath, paJingleEndPath) = RandomManager.Instance.RandomJingle();
EventReference jinglePath = PAMode ? paJinglePath : walkieJinglePath;
jingleInstance = CreateAndStartInstance(jinglePath, PAMode ? paObjects : new[] { GameManager.Instance.PlayerScript.gameObject }, PAMode);
}
public void TogglePAMode(bool PAMode)
{
isPAMode = PAMode;
if(PAMode)
{
for(int i = 0; i < paInstances.Length; i++)
{
ToggleMute(paInstances[i], false);
}
ToggleMute(walkieInstance, true);
}
else
{
for(int i = 0; i < paInstances.Length; i++)
{
ToggleMute(paInstances[i], true);
}
ToggleMute(walkieInstance, false);
}
}
private EventInstance CreateAndStartInstance(EventReference path, GameObject[] targets, bool mute)
{
EventInstance instance = RuntimeManager.CreateInstance(path);
foreach (var target in targets)
{
Globals.SpatialSounds(instance, target);
}
ToggleMute(instance, mute);
instance.setParameterByName(WalkieTalkieParam, mute ? 0 : 1);
instance.start();
return instance;
}
public void ActionPerformed(Transform transform, float value)
{
distance = Vector3.Distance(gameObject.transform.position, transform.position);
combined = distance / value;
if(PlayerRegistry.ActivePlayers.Contains(transform))
{
int index = PlayerRegistry.ActivePlayers.IndexOf(transform);
monster._lockedOn = PlayerRegistry.ActivePlayers[index];
}
else
{
return;
}
if(_threshold / 2.2f > combined && monster.monsterMode == MonsterMode.Idle ||
_threshold / 2.2f > combined && monster.monsterMode == MonsterMode.Patrol)
{
monster.ChangeMonsterState(MonsterMode.Chase);
}
else if(_threshold > combined && monster.monsterMode == MonsterMode.Idle)
{
monster.ChangeMonsterState(MonsterMode.Patrol);
}
if(_threshold > combined)
{
lastSoundTime = Time.time;
_lastSoundSource = transform;
_lastSoundPosition = _lastSoundSource.position;
}
}
private void Update()
{
if (!isMolerat) return;
if(monster.monsterMode != MonsterMode.Idle && Time.time - lastSoundTime > moleratForgetTime
&& Vector3.Distance(transform.position, _playerTransform.position) > soundDistance)
{
monster.ChangeMonsterState(MonsterMode.Idle);
}
if(monster.monsterMode != MonsterMode.Chase && Vector3.Distance(transform.position, _playerTransform.position) < 5f)
{
monster.ChangeMonsterState(MonsterMode.Chase);
}
}
public void PlayFMODEvent(int value)
{
eventInstance = FMODUnity.RuntimeManager.CreateInstance(events[value]);
Globals.SpatialSounds(eventInstance, gameObject);
eventInstance.start();
}
public void ReleaseFMODEvent()
{
eventInstance.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
eventInstance.release();
}
public void MarkLoopPoint()
{
Animator animator = GetComponent<Animator>();
if (animator == null) return;
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
_loopStateName = stateInfo.IsName("") ? null : stateInfo.shortNameHash.ToString();
_loopNormalizedTime = stateInfo.normalizedTime % 1f;
}
public void ReturnToLoopPoint()
{
Animator animator = GetComponent<Animator>();
if (animator == null || string.IsNullOrEmpty(_loopStateName)) return;
animator.Play(int.Parse(_loopStateName), 0, _loopNormalizedTime);
}
void OnDropdownChanged(int index)
{
Globals.Presets = (VRPresets)index;
if(index != 2) GiveSpecificSettings(index);
}
void OnSettingChanged(int index)
{
if(applyingPreset || index == 2 || index == 3) return;
_dropdown.SetValueWithoutNotify(2);
Globals.Presets = (VRPresets)2;
_dropdown.RefreshShownValue();
}
void GiveSpecificSettings(int index)
{
applyingPreset = true;
if(index == 0)
{
// Normal preset
dropdowns[0].value = 0;
dropdowns[1].value = 2;
dropdowns[3].value = 0;
dropdowns[4].value = 0;
dropdowns[5].value = 1;
}
else if(index == 1)
{
// Motion Sick preset
dropdowns[0].value = 1;
dropdowns[1].value = 1;
dropdowns[3].value = 0;
dropdowns[4].value = 2;
dropdowns[5].value = 0;
}
foreach(var dropdown in dropdowns)
{
dropdown.RefreshShownValue();
}
applyingPreset = false;
}
public class DialogueManager : Singleton<DialogueManager>
{
public void StartDialogue(Dialogue dialogue)
{
if(dialogue == null) return;
textbox = Instantiate(DialogueContainerPrefab, UIManager.Instance.CanvasTransform);
textbox.GetComponent<InfoGrab>().GrabInfo();
CurrentDialogue = dialogue;
CurrentIndex = 0;
InputManager.Instance.SwitchInputMaps("Player");
CurrentElement = CurrentDialogue.elements[CurrentIndex];
dynamicElement = null;
originalDialogue = true;
ShowNextDialogue();
}
public void EndDialogue()
{
StopAllCoroutines();
isTyping = false;
dialogueText.text = "";
speakerNameText.text = "";
Destroy(textbox);
}
}
private IEnumerator TypeText(string text, float speed, string name)
{
textbox.SetActive(true);
isTyping = true;
dialogueText.text = "";
foreach (char letter in text.ToCharArray())
{
if(!isTyping)
{
dialogueText.text = text.Replace("^", "").Replace("|", "").Replace("~", "");
break;
}
if(letter == '^')
{
InputManager.Instance.DisableInput();
if(!fastForward) yield return new WaitForSeconds(speed * 5);
InputManager.Instance.SwitchInputMaps("Player");
continue;
}
else if(letter == '|')
{
InputManager.Instance.DisableInput();
if(!fastForward) yield return new WaitForSeconds(speed * 10);
InputManager.Instance.SwitchInputMaps("Player");
continue;
}
else
{
dialogueText.text += letter;
}
if(!fastForward)
{
SoundManager.Instance.PlaySound(name);
yield return new WaitForSeconds(speed);
}
else
{
yield return null;
}
}
isTyping = false;
isWaiting = true;
}
private bool IsPromptValid(DynamicSettings[] requirements)
{
foreach(DynamicSettings requirement in requirements)
{
if(requirement.flag.flagName != "_None")
{
bool isFlagSet = FlagManager.Instance.CheckFlag(requirement.flag.flagName);
if(isFlagSet != requirement.isNotValid) return true;
continue;
}
if(requirement.item != null)
{
foreach(Item item in GameManager.Instance.Inventory)
{
if(item == requirement.item) return true;
}
continue;
}
Character targetCharacter = GameManager.Instance.GetCharacter(requirement.characterName);
if(targetCharacter == null) continue;
if(requirement.IntStat())
{
int currentStatValue = targetCharacter.GetStat(requirement.statType);
int currentStatValue2 = currentStatValue;
switch(requirement.statType)
{
case StatType.Aura:
currentStatValue *= targetCharacter.GetStat(StatType.WR);
break;
case StatType.IQ:
if(targetCharacter.GetStat(StatType.Goofy) <= currentStatValue)
currentStatValue2 -= targetCharacter.GetStat(StatType.Goofy);
else
currentStatValue2 = targetCharacter.GetStat(StatType.Goofy) - currentStatValue2;
break;
}
if(currentStatValue >= requirement.minThreshold && currentStatValue <= requirement.maxThreshold ||
currentStatValue2 >= requirement.minThreshold && currentStatValue2 <= requirement.maxThreshold)
{
return true;
}
}
}
return false;
}
public class FlagManager : Singleton<FlagManager>
{
private Dictionary<string, bool> activeFlags = new Dictionary<string, bool>();
// Call this to turn a flag on or off
public void SetFlag(string flagName, bool state)
{
activeFlags[flagName] = state;
UpdateDebugInspector();
}
// Call this to check if a flag is true. If it hasn't been set yet, it returns false.
public bool CheckFlag(string flagName)
{
if (activeFlags.TryGetValue(flagName, out bool state))
{
return state;
}
return false;
}
// Pulled by the save system to write every flag to the save file
public Dictionary<string, bool> GetAllFlags()
{
return activeFlags;
}
// Called on load to restore flags from that save file
public void RestoreSavedFlags(Dictionary<string, bool> savedFlags)
{
activeFlags = savedFlags ?? new Dictionary<string, bool>();
UpdateDebugInspector();
}
}
if(CurrentElement.modifyStats)
{
if(PlayerStats.IntStats.Contains(CurrentElement.statType))
{
ActiveCharacter.ModifyStat(CurrentElement.statType, CurrentElement.amount);
ActiveStatType = CurrentElement.statType;
if(CurrentElement.amount < 0)
{
GameManager.Instance.LevelDown(CurrentElement.statType, CurrentElement.amount, CurrentElement.bonusPoints);
}
else
{
GameManager.Instance.LevelUp(CurrentElement.statType, CurrentElement.amount, CurrentElement.bonusPoints);
}
CurrentIndex++;
return;
}
else if(PlayerStats.BoolStats.Contains(CurrentElement.statType))
{
ActiveCharacter.ModifyStat(CurrentElement.statType, CurrentElement.yayOrNay);
ActiveStatType = CurrentElement.statType;
}
}
else if(CurrentElement.dynamicCheck)
{
// Branches the conversation based on how one character's stat compares to another's
if(CurrentElement.characterTwoName != null && CurrentElement.characterTwoName != "")
{
Character charName = GameManager.Instance.GetCharacter(CurrentElement.characterTwoName);
if(ActiveCharacter.GetStat(CurrentElement.statType) > charName.GetStat(CurrentElement.statType) && !CurrentElement.lesserOrFalse ||
ActiveCharacter.GetStat(CurrentElement.statType) < charName.GetStat(CurrentElement.statType) && CurrentElement.lesserOrFalse)
{
DialogueOrDynamicElement(CurrentElement.dialogue);
return;
}
}
}
if(CurrentElement.branchingPaths != null && CurrentElement.branchingPaths.Length > 0)
{
// Deterministic pick across one of several possible dialogue branches
int seed = RandomGen.Get("Branch") % CurrentElement.branchingPaths.Length;
EndDialogue();
StartDialogue(CurrentElement.branchingPaths[seed]);
return;
}
else if(CurrentElement.converge != null)
{
// Regardless of which branch played, converge back into one shared dialogue
EndDialogue();
StartDialogue(CurrentElement.converge);
return;
}
if(CurrentElement.prompts != null && CurrentElement.prompts.Length > 0)
{
InputManager.Instance.SwitchInputMaps("UI");
EndDialogue();
InPrompt = true;
GameObject choicesPrompt = UIManager.Instance.ActualInstantiate(UIManager.Instance.ChoicesPrefab, UIManager.Instance.CanvasTransform);
UIController controller = choicesPrompt.GetComponent<UIController>();
Transform layoutTransform = choicesPrompt.transform.Find("Layout");
int first = 0;
foreach(PromptSettings prompt in CurrentElement.prompts)
{
// Skip any choice whose stat/inventory requirements aren't met
if(prompt.requirements != null && prompt.requirements.Length > 0 && !IsPromptValid(prompt.requirements))
{
continue;
}
GameObject buttonObject = UIManager.Instance.ActualInstantiate(UIManager.Instance.BasicButtonPrefab, layoutTransform);
Button button = buttonObject.GetComponent<Button>();
TMP_Text buttonText = buttonObject.GetComponentInChildren<TMP_Text>();
if(first == 0)
{
controller._defaultButton = buttonObject;
EventSystem.current.SetSelectedGameObject(buttonObject);
first++;
}
buttonText.text = prompt.name;
button.onClick.AddListener(() =>
{
Destroy(choicesPrompt);
StartDialogue(prompt.dialogue);
});
}
return;
}
using UnityEngine;
public class CreateManagers : MonoBehaviour
{
public GameObject[] managerPrefabs;
void Awake()
{
foreach(var prefab in managerPrefabs)
{
if(prefab != null)
{
string managerName = prefab.name;
if(FindObjectOfType(prefab.GetComponent<MonoBehaviour>().GetType()) == null)
{
GameObject obj = Instantiate(prefab);
obj.name = managerName;
DontDestroyOnLoad(obj);
}
}
}
}
}
public void ShowNextDialogueLine()
{
if(currentElementIndex < currentDialogue.elements.Length)
{
currentElement = currentDialogue.elements[currentElementIndex];
if(currentDialogue.dialogueType == DialogueType.Instant)
{
if(currentDialogue.audioClip != null && currentDialogue.audioClip != "")
{
vaInstance = FMODUnity.RuntimeManager.CreateInstance(currentDialogue.audioClip);
PlayAudio(vaInstance);
}
StartCoroutine(InstantDialogue());
}
else
{
if(!string.IsNullOrEmpty(currentElement.textBlipSound))
{
textBlipSoundInstance = FMODUnity.RuntimeManager.CreateInstance(currentElement.textBlipSound);
}
StartCoroutine(TypewriterDialogue(currentElement));
currentElementIndex++;
}
}
else StartCoroutine(UIDissapear());
}
public void ApplyImage(DialogueElement element, VisualElement image)
{
// Slides the speaker's sprite in from whichever side they're set to talk from
if(!element.rightSide)
{
image.AddToClassList("showUpLeft");
}
else
{
image.AddToClassList("showUpRight");
}
if(element.applySprite)
{
image.style.backgroundImage = new StyleBackground(element.sprite.texture);
}
else
{
image.style.backgroundImage = null;
}
}
public class ClassicalGenerationStrategy : IEraGenerationStrategy
{
private IRoomLogic[] roomLogics;
private int roomCap = 10;
private int[] specialClassicalRooms = { };
public ClassicalGenerationStrategy()
{
// Which room types are allowed to appear during this era
roomLogics = new IRoomLogic[]
{
new EnemyRoomLogic(),
};
}
public List<RoomType> GenerateEra()
{
List<RoomType> rooms = new List<RoomType>();
for (int i = 0; i < roomCap; i++)
{
int random = RandomGen.Get("RoomGenerator");
int index = random % roomLogics.Length;
rooms.Add(roomLogics[index].GetRoomType(random));
if (!RoomChecker.CheckRoom(rooms[i], roomLogics, specialClassicalRooms, i))
{
rooms[i] = RoomType.Enemy;
}
}
RoomChecker.ResetRoomCounts();
return rooms;
}
}