Implemented TileExtender
Fixed error spam with DoorwaySisters Added GenericScriptingParent Added failsafe to GlobalProp algorithm Removed DoorwayCleanup
This commit is contained in:
parent
11ddc7dea0
commit
8e30a1b6e6
@ -12,16 +12,20 @@ namespace DunGenPlus.Collections {
|
||||
public class DetailedGlobalPropSettings {
|
||||
|
||||
internal const string MinimumDistanceTooltip = "The minimum distance between each Global Props of this id.";
|
||||
internal const string GlobalCountMustBeReachedTooltip = "If true, when the global limit is not reached due to minimum distance, ignore it and try to reach the global limit anyway.";
|
||||
|
||||
public int ID;
|
||||
|
||||
[Space]
|
||||
[Tooltip(MinimumDistanceTooltip)]
|
||||
public float MinimumDistance;
|
||||
public bool GlobalCountMustBeReached = true;
|
||||
|
||||
public DetailedGlobalPropSettings(int id, float minimumDistance) {
|
||||
public DetailedGlobalPropSettings(int id, float minimumDistance, bool globalCountMustBeReached)
|
||||
{
|
||||
ID = id;
|
||||
MinimumDistance = minimumDistance;
|
||||
GlobalCountMustBeReached = globalCountMustBeReached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ namespace DunGenPlus.Collections {
|
||||
public class LocalGlobalPropSettings {
|
||||
|
||||
internal const string GlobalPropLimitTooltip = "If true, when PostProcess reaches the local limit of Global Props for all main paths but does not reach the global limit, use the remaining props in this main path to reach the global limit.";
|
||||
internal const string MinimumDistanceBetweenPropsTooltip = "If true, Global Props of this id MUST have a minimum distance between each other.";
|
||||
|
||||
public int ID;
|
||||
|
||||
|
@ -13,35 +13,51 @@ namespace DunGenPlus.Collections {
|
||||
public TileExtender PrefabTileExtender { get; internal set; }
|
||||
public List<DoorwayProxy> Entrances { get; internal set; }
|
||||
public List<DoorwayProxy> Exits { get; internal set; }
|
||||
public List<DoorwayProxy> OverlappingDoorways { get; internal set; }
|
||||
public bool EntranceExitInterchangable { get; internal set; }
|
||||
|
||||
public TileExtenderProxy(TileExtenderProxy existingTileExtenderProxy) {
|
||||
TileProxy = existingTileExtenderProxy.TileProxy;
|
||||
public TileExtenderProxy(TileProxy tileProxy, TileExtenderProxy existingTileExtenderProxy) {
|
||||
TileProxy = tileProxy;
|
||||
PrefabTileExtender = existingTileExtenderProxy.PrefabTileExtender;
|
||||
|
||||
Entrances = new List<DoorwayProxy>();
|
||||
Exits = new List<DoorwayProxy>();
|
||||
OverlappingDoorways = new List<DoorwayProxy>();
|
||||
|
||||
foreach(var existingDoorway in TileProxy.doorways) {
|
||||
if (existingTileExtenderProxy.Entrances.Contains(existingDoorway)) Entrances.Add(existingDoorway);
|
||||
if (existingTileExtenderProxy.Exits.Contains(existingDoorway)) Exits.Add(existingDoorway);
|
||||
EntranceExitInterchangable = existingTileExtenderProxy.EntranceExitInterchangable;
|
||||
|
||||
var existingTile = existingTileExtenderProxy.TileProxy;
|
||||
for(var i = 0; i < tileProxy.doorways.Count; ++i){
|
||||
var doorway = tileProxy.doorways[i];
|
||||
var existingDoorway = existingTile.doorways[i];
|
||||
if (existingTileExtenderProxy.Entrances.Contains(existingDoorway)) Entrances.Add(doorway);
|
||||
if (existingTileExtenderProxy.Exits.Contains(existingDoorway)) Exits.Add(doorway);
|
||||
if (existingTileExtenderProxy.OverlappingDoorways.Contains(existingDoorway)) OverlappingDoorways.Add(doorway);
|
||||
}
|
||||
}
|
||||
|
||||
public TileExtenderProxy(TileProxy tileProxy) {
|
||||
TileProxy = tileProxy;
|
||||
PrefabTileExtender = tileProxy.Prefab.GetComponent<TileExtender>();
|
||||
|
||||
Entrances = new List<DoorwayProxy>();
|
||||
Exits = new List<DoorwayProxy>();
|
||||
OverlappingDoorways = new List<DoorwayProxy>();
|
||||
|
||||
if (PrefabTileExtender == null) {
|
||||
if (tileProxy.Entrance != null) Entrances.Add(tileProxy.Entrance);
|
||||
if (tileProxy.Exit != null) Exits.Add(tileProxy.Exit);
|
||||
EntranceExitInterchangable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var proxyDoorway in tileProxy.doorways) {
|
||||
if (PrefabTileExtender.entrances.Contains(proxyDoorway.DoorwayComponent)) Entrances.Add(proxyDoorway);
|
||||
if (PrefabTileExtender.exits.Contains(proxyDoorway.DoorwayComponent)) Exits.Add(proxyDoorway);
|
||||
if (PrefabTileExtender.overlappingDoorways.Contains(proxyDoorway.DoorwayComponent)) OverlappingDoorways.Add(proxyDoorway);
|
||||
}
|
||||
|
||||
EntranceExitInterchangable = PrefabTileExtender.entranctExitInterchangable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +0,0 @@
|
||||
using DunGen;
|
||||
using DunGenPlus.Components.DoorwayCleanupScripting;
|
||||
using DunGenPlus.Managers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components {
|
||||
public class DoorwayCleanup : MonoBehaviour, IDungeonCompleteReceiver {
|
||||
|
||||
[Header("OBSOLUTE. Please use DoorwayScriptingParent")]
|
||||
[Header("Doorway References")]
|
||||
[Tooltip("The doorway reference.")]
|
||||
public Doorway doorway;
|
||||
[Tooltip("The connectors scene objects of the doorway.\n\nHighly advise to empty the corresponding list in the doorway.")]
|
||||
public List<GameObject> connectors;
|
||||
[Tooltip("The blockers scene objects of the doorway.\n\nHighly advise to empty the corresponding list in the doorway.")]
|
||||
public List<GameObject> blockers;
|
||||
[Tooltip("The doorway gameobject target for the DoorwayCleanupScripts. Can be null.")]
|
||||
public GameObject doorwayGameObject;
|
||||
|
||||
[Header("Overrides")]
|
||||
[Tooltip("Mainly for code purposes. Forces the connectors to be active.")]
|
||||
public bool overrideConnector;
|
||||
[Tooltip("Mainly for code purposes. Forces the blockers to be active.")]
|
||||
public bool overrideBlocker;
|
||||
[Tooltip("Mainly for code purposes. Forces the doorway gameobject to be disabled.")]
|
||||
public bool overrideNoDoorway;
|
||||
|
||||
public void OnDungeonComplete(Dungeon dungeon) {
|
||||
SetBlockers(true);
|
||||
DoorwayManager.AddDoorwayCleanup(this);
|
||||
}
|
||||
|
||||
public void Cleanup(){
|
||||
// start up like in original
|
||||
SwitchConnectorBlocker(doorway.ConnectedDoorway != null);
|
||||
|
||||
var cleanupList = GetComponentsInChildren<DoorwayCleanupScript>();
|
||||
foreach(var c in cleanupList) c.Cleanup(this);
|
||||
|
||||
if (overrideNoDoorway) SwitchDoorwayGameObject(false);
|
||||
|
||||
// clean up like in original
|
||||
foreach(var c in connectors){
|
||||
if (!c.activeSelf) UnityEngine.Object.DestroyImmediate(c, false);
|
||||
}
|
||||
|
||||
foreach(var b in blockers){
|
||||
if (!b.activeSelf) UnityEngine.Object.DestroyImmediate(b, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBlockers(bool state){
|
||||
foreach(var b in blockers) b.SetActive(state);
|
||||
}
|
||||
|
||||
public void SwitchConnectorBlocker(bool isConnector){
|
||||
if (overrideConnector) isConnector = true;
|
||||
if (overrideBlocker) isConnector = false;
|
||||
|
||||
foreach(var c in connectors) c.SetActive(isConnector);
|
||||
foreach(var b in blockers) b.SetActive(!isConnector);
|
||||
}
|
||||
|
||||
public void SwitchDoorwayGameObject(bool isActive){
|
||||
doorwayGameObject?.SetActive(isActive);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public class DCSConnectorBlockerSpawnedPrefab : DoorwayCleanupScript {
|
||||
|
||||
public enum Action { SwitchToConnector, SwitchToBlocker };
|
||||
|
||||
[Header("Calls switch action\nif Doorway instantiates a Connector/Blocker prefab with the target's name")]
|
||||
[Header("Switch Action")]
|
||||
public Action switchAction;
|
||||
|
||||
[Header("Target")]
|
||||
public GameObject target;
|
||||
|
||||
public override void Cleanup(DoorwayCleanup parent) {
|
||||
var result = false;
|
||||
foreach(Transform t in parent.doorway.transform){
|
||||
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
parent.SwitchConnectorBlocker(switchAction == Action.SwitchToConnector);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public class DCSRemoveDoorwayConnectedDoorway : DoorwayCleanupScriptDoorwayCompare {
|
||||
|
||||
[Header("Removes Doorway Gameobject\nif the neighboring doorway's priority matches the operation comparison")]
|
||||
[Header("Operation Comparison")]
|
||||
public int doorwayPriority;
|
||||
public int doorwayPriorityB;
|
||||
public Operation operation = Operation.Equal;
|
||||
|
||||
public override void Cleanup(DoorwayCleanup parent) {
|
||||
var doorway = parent.doorway;
|
||||
if (doorway.connectedDoorway == null) return;
|
||||
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB));
|
||||
|
||||
if (result) {
|
||||
parent.SwitchDoorwayGameObject(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public class DCSRemoveDoorwaySpawnedPrefab : DoorwayCleanupScript {
|
||||
|
||||
[Header("Removes Doorway Gameobject\nif Doorway instantiates a Connector/Blocker prefab with the target's name")]
|
||||
[Header("Target")]
|
||||
public GameObject target;
|
||||
|
||||
public override void Cleanup(DoorwayCleanup parent) {
|
||||
var result = false;
|
||||
foreach(Transform t in parent.doorway.transform){
|
||||
if (t.gameObject.activeSelf && t.name.Contains(target.name)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
parent.SwitchDoorwayGameObject(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public class DCSRemoveGameObjectsConnectedDoorway : DoorwayCleanupScriptDoorwayCompare {
|
||||
|
||||
[Header("Removes target GameObjects\nif the neighboring doorway's priority matches the operation comparison")]
|
||||
|
||||
[Header("Operation Comparison")]
|
||||
public int doorwayPriority;
|
||||
public int doorwayPriorityB;
|
||||
public Operation operation = Operation.Equal;
|
||||
|
||||
[Header("Targets")]
|
||||
public List<GameObject> targets;
|
||||
|
||||
public override void Cleanup(DoorwayCleanup parent) {
|
||||
var doorway = parent.doorway;
|
||||
if (doorway.connectedDoorway == null) return;
|
||||
var result = GetOperation(operation).Invoke(doorway.connectedDoorway, new Arguments(doorwayPriority, doorwayPriorityB));
|
||||
if (result) {
|
||||
foreach(var t in targets) t.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public abstract class DoorwayCleanupScript : MonoBehaviour {
|
||||
|
||||
public abstract void Cleanup(DoorwayCleanup parent);
|
||||
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
using DunGen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.DoorwayCleanupScripting {
|
||||
[Obsolete("Please use DoorwayScriptingParent")]
|
||||
public abstract class DoorwayCleanupScriptDoorwayCompare : DoorwayCleanupScript {
|
||||
|
||||
public enum Operation {
|
||||
[InspectorName("Equal (target == value)")]
|
||||
Equal,
|
||||
[InspectorName("NotEqual (target != value)")]
|
||||
NotEqual,
|
||||
[InspectorName("LessThan (target < value)")]
|
||||
LessThan,
|
||||
[InspectorName("GreaterThan (target > value)")]
|
||||
GreaterThan,
|
||||
[InspectorName("LessThanEq (target <= value)")]
|
||||
LessThanEq,
|
||||
[InspectorName("GreaterThanEw (target >= value)")]
|
||||
GreaterThanEq,
|
||||
[InspectorName("Between (value < target < valueB)")]
|
||||
Between,
|
||||
[InspectorName("BetweenEq (value <= target <= valueB)")]
|
||||
BetweenEq
|
||||
}
|
||||
|
||||
public struct Arguments{
|
||||
public int parameterA;
|
||||
public int parameterB;
|
||||
|
||||
public Arguments(int parameterA, int parameterB){
|
||||
this.parameterA = parameterA;
|
||||
this.parameterB = parameterB;
|
||||
}
|
||||
|
||||
public static explicit operator Arguments((int a, int b) pair) => new Arguments(pair.a, pair.b);
|
||||
}
|
||||
|
||||
public Func<Doorway, Arguments, bool> GetOperation(Operation operation){
|
||||
switch(operation){
|
||||
case Operation.Equal:
|
||||
return EqualOperation;
|
||||
case Operation.NotEqual:
|
||||
return NotEqualOperation;
|
||||
case Operation.LessThan:
|
||||
return LessThanOperation;
|
||||
case Operation.GreaterThan:
|
||||
return GreaterThanOperation;
|
||||
case Operation.LessThanEq:
|
||||
return LessThanEqualOperation;
|
||||
case Operation.GreaterThanEq:
|
||||
return GreaterThanEqualOperation;
|
||||
case Operation.Between:
|
||||
return BetweenOperation;
|
||||
case Operation.BetweenEq:
|
||||
return BetweenEqualOperation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool EqualOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority == arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool NotEqualOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority != arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool LessThanOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority < arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool GreaterThanOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority > arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool LessThanEqualOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority <= arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool GreaterThanEqualOperation(Doorway other, Arguments arguments){
|
||||
return other.DoorPrefabPriority >= arguments.parameterA;
|
||||
}
|
||||
|
||||
public bool BetweenOperation(Doorway other, Arguments arguments){
|
||||
return arguments.parameterA < other.DoorPrefabPriority && other.DoorPrefabPriority < arguments.parameterB;
|
||||
}
|
||||
|
||||
public bool BetweenEqualOperation(Doorway other, Arguments arguments){
|
||||
return arguments.parameterA <= other.DoorPrefabPriority && other.DoorPrefabPriority <= arguments.parameterB;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ namespace DunGenPlus.Components {
|
||||
}
|
||||
|
||||
[Tooltip("The list of 'sister' doorways.\n\nUseDoorwaySisters must be toggled in DunGenExtender for this component to be used.\n\nThis doorway will not generate if it's an intersecting doorway, any of it's 'sister' doorways are generated, and both this doorway and the 'sister' doorway lead to the same tile.")]
|
||||
public List<Doorway> sisters;
|
||||
public List<Doorway> sisters = new List<Doorway>();
|
||||
|
||||
void OnValidate(){
|
||||
var sis = sisters.Select(s => s.GetComponent<DoorwaySisters>());
|
||||
|
@ -55,9 +55,8 @@ namespace DunGenPlus.Components.Scripting {
|
||||
}
|
||||
|
||||
public override EvaluationContext CreateContext() {
|
||||
var context = new EvaluationContext(GetFields);
|
||||
var context = base.CreateContext();
|
||||
context.RegisterFunction("doorwaySpawnedGameObject", new FunctionRoutine(2, doorwaySpawnedGameObjectFunction));
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
@ -73,7 +72,7 @@ namespace DunGenPlus.Components.Scripting {
|
||||
return ExpressionToken.False;
|
||||
}
|
||||
|
||||
(object, ValueTypeHint) GetFields(string field) {
|
||||
public override (object, ValueTypeHint) GetFields(string field) {
|
||||
var split = field.Split('.');
|
||||
|
||||
if (split.Length <= 1) {
|
||||
|
@ -124,6 +124,13 @@ namespace DunGenPlus.Components.Scripting {
|
||||
}
|
||||
}
|
||||
|
||||
public NamedGameObjectReference GetNamedGameObject(string name){
|
||||
if (namedDictionary.TryGetValue(name, out var obj)){
|
||||
return obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetNamedGameObjectOverrideState(string name, OverrideGameObjectState state){
|
||||
if (namedDictionary.TryGetValue(name, out var obj)){
|
||||
obj.overrideState = state;
|
||||
@ -138,15 +145,34 @@ namespace DunGenPlus.Components.Scripting {
|
||||
}
|
||||
}
|
||||
|
||||
protected bool CheckIfNotNull(object target, string name){
|
||||
if (target == null) {
|
||||
Utils.Utility.PrintLog($"{name} was null", BepInEx.Logging.LogLevel.Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
public virtual EvaluationContext CreateContext() {
|
||||
var context = new EvaluationContext(GetFields);
|
||||
context.RegisterFunction("isAnyActive", new FunctionRoutine(1, isAnyActiveFunction));
|
||||
context.RegisterFunction("isAllActive", new FunctionRoutine(1, isAllActiveFunction));
|
||||
return context;
|
||||
}
|
||||
|
||||
public abstract EvaluationContext CreateContext();
|
||||
public virtual (object, ValueTypeHint) GetFields(string field) {
|
||||
return default;
|
||||
}
|
||||
|
||||
ExpressionToken isAnyActiveFunction(EvaluationContext context, ExpressionToken[] parameters) {
|
||||
var targetName = parameters[0].Value;
|
||||
var target = GetNamedGameObject(targetName);
|
||||
if (target != null) {
|
||||
return target.gameObjects.Any(x => x != null && x.activeSelf) ? ExpressionToken.True : ExpressionToken.False;
|
||||
}
|
||||
return ExpressionToken.False;
|
||||
}
|
||||
|
||||
ExpressionToken isAllActiveFunction(EvaluationContext context, ExpressionToken[] parameters) {
|
||||
var targetName = parameters[0].Value;
|
||||
var target = GetNamedGameObject(targetName);
|
||||
if (target != null) {
|
||||
return target.gameObjects.All(x => x != null && x.activeSelf) ? ExpressionToken.True : ExpressionToken.False;
|
||||
}
|
||||
return ExpressionToken.False;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
using DunGen;
|
||||
using Soukoku.ExpressionParser;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DunGenPlus.Components.Scripting
|
||||
{
|
||||
internal class GenericScriptingParent : DunGenPlusScriptingParent<Transform> {
|
||||
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ namespace DunGenPlus.Components {
|
||||
|
||||
public List<Doorway> entrances = new List<Doorway>();
|
||||
public List<Doorway> exits = new List<Doorway>();
|
||||
public bool entranctExitInterchangable;
|
||||
public List<Doorway> overlappingDoorways = new List<Doorway>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -149,13 +149,6 @@
|
||||
<Compile Include="Collections\NodeArchetype.cs" />
|
||||
<Compile Include="Collections\NullObject.cs" />
|
||||
<Compile Include="Collections\TileExtenderProxy.cs" />
|
||||
<Compile Include="Components\DoorwayCleanup.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveDoorwayConnectedDoorway.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveDoorwaySpawnedPrefab.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DCSRemoveGameObjectsConnectedDoorway.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DoorwayCleanupScript.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DCSConnectorBlockerSpawnedPrefab.cs" />
|
||||
<Compile Include="Components\DoorwayCleanupScripting\DoorwayCleanupScriptDoorwayCompare.cs" />
|
||||
<Compile Include="Components\DoorwaySisters.cs" />
|
||||
<Compile Include="Components\MainRoomDoorwayGroups.cs" />
|
||||
<Compile Include="Components\Props\SpawnSyncedObjectCycle.cs" />
|
||||
@ -163,6 +156,7 @@
|
||||
<Compile Include="Components\Scripting\DoorwayScriptingParent.cs" />
|
||||
<Compile Include="Components\Scripting\DunGenPlusScript.cs" />
|
||||
<Compile Include="Components\Scripting\DunGenPlusScriptingParent.cs" />
|
||||
<Compile Include="Components\Scripting\GenericScriptingParent.cs" />
|
||||
<Compile Include="DevTools\Panels\AssetsPanel.cs" />
|
||||
<Compile Include="ExpressionParser\EvaluationContext.cs" />
|
||||
<Compile Include="ExpressionParser\Evaluator.cs" />
|
||||
@ -185,6 +179,7 @@
|
||||
<Compile Include="Managers\EnemyManager.cs" />
|
||||
<Compile Include="Managers\ScrapItemManager.cs" />
|
||||
<Compile Include="Patches\BranchCountHelperPatch.cs" />
|
||||
<Compile Include="Patches\DoorwayPairFinderPatch.cs" />
|
||||
<Compile Include="Patches\DungeonPatch.cs" />
|
||||
<Compile Include="Patches\TileProxyPatch.cs" />
|
||||
<Compile Include="PluginConfig.cs" />
|
||||
|
Binary file not shown.
@ -14,6 +14,7 @@ namespace DunGenPlus.Generation {
|
||||
internal partial class DunGenPlusGenerator {
|
||||
|
||||
// Copied and pasted from DunGen
|
||||
// And heavily franksteined
|
||||
public static void ProcessGlobalPropsPerMainPath(DungeonGenerator dungeonGenerator){
|
||||
var localIDs = Properties.MainPathProperties.MainPathDetails.SelectMany(d => d.LocalGroupProps).Select(x => x.ID).ToHashSet();
|
||||
var detailedSettings = Properties.MainPathProperties.DetailedGlobalPropSettings;
|
||||
@ -23,6 +24,7 @@ namespace DunGenPlus.Generation {
|
||||
var localDictionary = new Dictionary<int, Dictionary<int, GameObjectChanceTable>>();
|
||||
|
||||
// default dictionary
|
||||
// first parmeter int is the Global Prop ID
|
||||
var globalDictionary = new Dictionary<int, GameObjectChanceTable>();
|
||||
|
||||
foreach(var tile in dungeonGenerator.CurrentDungeon.AllTiles){
|
||||
@ -54,6 +56,7 @@ namespace DunGenPlus.Generation {
|
||||
}
|
||||
}
|
||||
|
||||
// deactive in preparation
|
||||
foreach(var dictionary in localDictionary.Values){
|
||||
foreach(var table2 in dictionary.Values){
|
||||
foreach(var gameObjectChance in table2.Weights){
|
||||
@ -70,7 +73,8 @@ namespace DunGenPlus.Generation {
|
||||
|
||||
var list = new List<int>(localDictionary.Count + globalDictionary.Count);
|
||||
|
||||
void RemoveElementsTooClose(GameObjectChanceTable table, Vector3 position, float minDistance){
|
||||
// min distance behaviour
|
||||
void RemoveElementsTooClose(GameObjectChanceTable table, GameObjectChanceTable reserveTable, Vector3 position, float minDistance){
|
||||
if (minDistance <= 0f) return;
|
||||
|
||||
var elementsToRemove = new List<GameObjectChance>();
|
||||
@ -82,33 +86,40 @@ namespace DunGenPlus.Generation {
|
||||
|
||||
foreach(var e in elementsToRemove){
|
||||
table.Weights.Remove(e);
|
||||
reserveTable.Weights.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
int ProcessGlobalPropID(GameObjectChanceTable table, int localMax, int globalCount, int globalMax, float minDistance){
|
||||
// normal default behaviour
|
||||
int ProcessGlobalPropID(GameObjectChanceTable table, GameObjectChanceTable reserveTable, int localMax, int globalCount, int globalMax, float minDistance){
|
||||
localMax = Mathf.Clamp(localMax, 0, table.Weights.Count);
|
||||
var i = 0;
|
||||
while(i < localMax && i + globalCount < globalMax){
|
||||
if (table.Weights.Count == 0) return i;
|
||||
|
||||
var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true);
|
||||
if (random != null && random.Value != null) {
|
||||
random.Value.SetActive(true);
|
||||
RemoveElementsTooClose(table, random.Value.transform.position, minDistance);
|
||||
RemoveElementsTooClose(table, reserveTable, random.Value.transform.position, minDistance);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, int count, float minDistance){
|
||||
// UseToReachGlobalPropLimit behaviour
|
||||
int ProcessRemainingGlobalPropID(GameObjectChanceTable[] tables, GameObjectChanceTable reserveTable, int count, float minDistance){
|
||||
count = Mathf.Clamp(count, 0, tables.Sum(t => t.Weights.Count));
|
||||
var i = 0;
|
||||
while(i < count){
|
||||
if (tables.Sum(t => t.Weights.Count) == 0) return i;
|
||||
|
||||
var random = GameObjectChanceTable.GetCombinedRandom(dungeonGenerator.RandomStream, true, 0f, tables);
|
||||
if (random != null) {
|
||||
random.SetActive(true);
|
||||
|
||||
foreach(var t in tables){
|
||||
RemoveElementsTooClose(t, random.transform.position, minDistance);
|
||||
RemoveElementsTooClose(t, reserveTable, random.transform.position, minDistance);
|
||||
}
|
||||
}
|
||||
++i;
|
||||
@ -116,13 +127,29 @@ namespace DunGenPlus.Generation {
|
||||
return i;
|
||||
}
|
||||
|
||||
// cause min distance can mean that global prop limit is not reached
|
||||
// fuck it, make it happen
|
||||
int ProcessRemainingGlobalPropIDNoMatterWhat(GameObjectChanceTable table, int count){
|
||||
count = Mathf.Clamp(count, 0, table.Weights.Count);
|
||||
var i = 0;
|
||||
while(i < count){
|
||||
if (table.Weights.Count == 0) return i;
|
||||
|
||||
var random = table.GetRandom(dungeonGenerator.RandomStream, true, 0f, null, true, true);
|
||||
if (random != null) {
|
||||
random.Value.SetActive(true);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
using(var enumerator = globalDictionary.GetEnumerator()){
|
||||
while(enumerator.MoveNext()){
|
||||
var pair = enumerator.Current;
|
||||
if (list.Contains(pair.Key)){
|
||||
Plugin.logger.LogWarning("Dungeon Flow contains multiple entries for the global prop group ID: " + pair.Key.ToString() + ". Only the first entry will be used.");
|
||||
} else {
|
||||
//Plugin.logger.LogWarning($"{pair.Key}: Global");
|
||||
|
||||
var globalPropSettings = dungeonGenerator.DungeonFlow.GlobalProps
|
||||
.Where(x => x.ID == pair.Key)
|
||||
@ -131,15 +158,25 @@ namespace DunGenPlus.Generation {
|
||||
var detailedPropSettings = detailedSettings
|
||||
.Where(x => x.ID == pair.Key)
|
||||
.FirstOrDefault();
|
||||
|
||||
var reserveTable = new GameObjectChanceTable();
|
||||
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
|
||||
var reachGlobalLimitNoMatterWhat = detailedPropSettings != null ? detailedPropSettings.GlobalCountMustBeReached : true;
|
||||
|
||||
if (globalPropSettings != null){
|
||||
var tableClone = pair.Value.Clone();
|
||||
var table = pair.Value;
|
||||
var globalMax = globalPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
|
||||
|
||||
var spawned = ProcessGlobalPropID(tableClone, globalMax, 0, globalMax, minDistance);
|
||||
var spawned = ProcessGlobalPropID(table, reserveTable, globalMax, 0, globalMax, minDistance);
|
||||
Plugin.logger.LogDebug($"Global ID: {pair.Key} ({spawned} / {globalMax}). Min Dist: {minDistance}");
|
||||
list.Add(pair.Key);
|
||||
|
||||
// failsafe
|
||||
if (reachGlobalLimitNoMatterWhat && spawned < globalMax) {
|
||||
Plugin.logger.LogDebug($"MinDistance be damned, forcing {globalMax - spawned} more to spawn");
|
||||
spawned += ProcessRemainingGlobalPropIDNoMatterWhat(reserveTable, globalMax - spawned);
|
||||
Plugin.logger.LogDebug($"Spawned remaining props ({spawned} / {globalMax})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +198,11 @@ namespace DunGenPlus.Generation {
|
||||
var detailedPropSettings = detailedSettings
|
||||
.Where(x => x.ID == pair.Key)
|
||||
.FirstOrDefault();
|
||||
|
||||
var reserveTable = new GameObjectChanceTable();
|
||||
var minDistance = detailedPropSettings != null ? detailedPropSettings.MinimumDistance : -1f;
|
||||
var reachGlobalLimitNoMatterWhat = detailedPropSettings != null ? detailedPropSettings.GlobalCountMustBeReached : true;
|
||||
reachGlobalLimitNoMatterWhat = true;
|
||||
|
||||
if (globalPropSettings != null){
|
||||
var globalCount = 0;
|
||||
@ -172,8 +213,6 @@ namespace DunGenPlus.Generation {
|
||||
var toRemoveKeys = new List<int>();
|
||||
|
||||
foreach(var pathPair in pathDictionary){
|
||||
|
||||
|
||||
// try and get local main path properites based on key of Dictionary<MainPathIndex (int), GlobalProps (GameObjectChanceTable)>
|
||||
var mainPathIndex = pathPair.Key;
|
||||
var localGroupProps = Properties.MainPathProperties.GetMainPathDetails(mainPathIndex).LocalGroupProps;
|
||||
@ -186,15 +225,20 @@ namespace DunGenPlus.Generation {
|
||||
continue;
|
||||
}
|
||||
|
||||
var tableClone = pathPair.Value.Clone();
|
||||
var tableClone = pathPair.Value; // this was cloned, why?
|
||||
var localMax = localPropSettings.Count.GetRandom(dungeonGenerator.RandomStream);
|
||||
|
||||
var spawned = ProcessGlobalPropID(tableClone, localMax, globalCount, globalMax, minDistance);
|
||||
var spawned = ProcessGlobalPropID(tableClone, reserveTable, localMax, globalCount, globalMax, minDistance);
|
||||
globalCount += spawned;
|
||||
Plugin.logger.LogDebug($"Main Path {mainPathIndex}: Local ({spawned} / {localMax}), Global ({globalCount} / {globalMax})");
|
||||
|
||||
// all dictionaries that we done using get throw out
|
||||
if (!localPropSettings.UseToReachGlobalPropLimit) toRemoveKeys.Add(mainPathIndex);
|
||||
if (!localPropSettings.UseToReachGlobalPropLimit) {
|
||||
toRemoveKeys.Add(mainPathIndex);
|
||||
foreach(var w in tableClone.Weights){
|
||||
reserveTable.Weights.Add(w); // but we still have to include anything remaining back into the reserve
|
||||
}
|
||||
}
|
||||
|
||||
//Plugin.logger.LogError($"Spawned {spawned} ({globalCount}/{globalMax})");
|
||||
}
|
||||
@ -209,11 +253,18 @@ namespace DunGenPlus.Generation {
|
||||
Plugin.logger.LogDebug($"Combining main paths ({combine}) into one GameObjectChanceTable");
|
||||
|
||||
var combinedTable = pathDictionary.Select(d => d.Value).ToArray();
|
||||
var spawned = ProcessRemainingGlobalPropID(combinedTable, globalMax - globalCount, minDistance);
|
||||
var spawned = ProcessRemainingGlobalPropID(combinedTable, reserveTable, globalMax - globalCount, minDistance);
|
||||
globalCount += spawned;
|
||||
Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})");
|
||||
}
|
||||
|
||||
// failsafe
|
||||
if (reachGlobalLimitNoMatterWhat && globalCount < globalMax) {
|
||||
Plugin.logger.LogDebug($"MinDistance be damned, forcing {globalMax - globalCount} more to spawn");
|
||||
globalCount += ProcessRemainingGlobalPropIDNoMatterWhat(reserveTable, globalMax - globalCount);
|
||||
Plugin.logger.LogDebug($"Spawned remaining props ({globalCount} / {globalMax})");
|
||||
}
|
||||
|
||||
list.Add(pair.Key);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using DunGen;
|
||||
using DunGen.Graph;
|
||||
using DunGenPlus.DevTools;
|
||||
using DunGenPlus.Patches;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -95,5 +96,94 @@ namespace DunGenPlus.Generation {
|
||||
return defaultState || API.IsDevDebugModeActive();
|
||||
}
|
||||
|
||||
public static IEnumerable<DoorwayPair> GetPotentialDoorwayPairsForNonFirstTileAlternate(DoorwayPairFinder __instance){
|
||||
var previousTile = __instance.PreviousTile;
|
||||
var previousTileExtended = TileProxyPatch.GetTileExtenderProxy(previousTile);
|
||||
|
||||
var tileOrder = __instance.tileOrder;
|
||||
|
||||
|
||||
IEnumerable<DoorwayProxy> validPrevExits;
|
||||
// default base behaviour
|
||||
if (!previousTileExtended.EntranceExitInterchangable){
|
||||
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Exits);
|
||||
}
|
||||
// interchangable behaviour
|
||||
else {
|
||||
// check if previous tile used a specific exit/entrance
|
||||
// they used an exit as their entrance, so use entrances
|
||||
if (previousTile.UsedDoorways.Intersect(previousTileExtended.Exits).Any()){
|
||||
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Entrances);
|
||||
}
|
||||
// they used an entrance as their entrance, so use exits
|
||||
else if (previousTile.UsedDoorways.Intersect(previousTileExtended.Entrances).Any()){
|
||||
validPrevExits = previousTile.UnusedDoorways.Intersect(previousTileExtended.Exits);
|
||||
}
|
||||
// uhh i guess it's fine?
|
||||
else {
|
||||
validPrevExits = new List<DoorwayProxy>();
|
||||
}
|
||||
}
|
||||
|
||||
var requiresSpecExit = validPrevExits.Any();
|
||||
|
||||
foreach(var previousDoor in previousTile.UnusedDoorways) {
|
||||
// overlapping doors aren't allowed to be potential doorway pairs since this function only finds entrance/exits
|
||||
// or have fun
|
||||
var isPrevOverlappingDoor = previousTileExtended.OverlappingDoorways.Contains(previousDoor);
|
||||
if (isPrevOverlappingDoor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only use allowed doorway exits
|
||||
if (requiresSpecExit && !validPrevExits.Contains(previousDoor)) continue;
|
||||
|
||||
foreach(var tileWeight in __instance.TileWeights) {
|
||||
// skip if not ever considered
|
||||
if (!tileOrder.Contains(tileWeight)) continue;
|
||||
|
||||
var nextTile = __instance.GetTileTemplateDelegate(tileWeight.Value);
|
||||
var nextTileExtended = TileProxyPatch.GetTileExtenderProxy(nextTile);
|
||||
var weight = (float)(tileOrder.Count - tileOrder.IndexOf(tileWeight));
|
||||
|
||||
if (__instance.IsTileAllowedPredicate != null && !__instance.IsTileAllowedPredicate(previousTile, nextTile, ref weight)) continue;
|
||||
|
||||
foreach(var nextDoor in nextTile.Doorways) {
|
||||
// check for previous and next
|
||||
// i forget next which causes problems obviously
|
||||
var isNextOverlappingDoor = nextTileExtended.OverlappingDoorways.Contains(nextDoor);
|
||||
if (isNextOverlappingDoor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool AllowEntranceAndExitPair(IEnumerable<DoorwayProxy> entrances, IEnumerable<DoorwayProxy> exits){
|
||||
// only use allowed doorway entrances
|
||||
if (entrances.Any() && !entrances.Contains(nextDoor)) return false;
|
||||
// skip if desiganted as an exit
|
||||
if (exits.Contains(nextDoor)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// normal default behaviour
|
||||
if (!nextTileExtended.EntranceExitInterchangable){
|
||||
if (!AllowEntranceAndExitPair(nextTileExtended.Entrances, nextTileExtended.Exits)) continue;
|
||||
}
|
||||
// interchangable behaviour
|
||||
else {
|
||||
var firstCheck = AllowEntranceAndExitPair(nextTileExtended.Entrances, nextTileExtended.Exits);
|
||||
var secondCheck = AllowEntranceAndExitPair(nextTileExtended.Exits, nextTileExtended.Entrances);
|
||||
if (!firstCheck && !secondCheck) continue;
|
||||
}
|
||||
|
||||
var doorwayWeight = 0f;
|
||||
if (__instance.IsValidDoorwayPairing(previousDoor, nextDoor, previousTile, nextTile, ref doorwayWeight))
|
||||
yield return new DoorwayPair(previousTile, previousDoor, nextTile, nextDoor, tileWeight.TileSet, weight, doorwayWeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ using DunGenPlus.Components;
|
||||
using DunGenPlus.Components.Scripting;
|
||||
using DunGenPlus.Generation;
|
||||
using DunGenPlus.Utils;
|
||||
using static DunGenPlus.Managers.DoorwayManager;
|
||||
|
||||
namespace DunGenPlus.Managers {
|
||||
public static class DoorwayManager {
|
||||
@ -59,10 +58,6 @@ namespace DunGenPlus.Managers {
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddDoorwayCleanup(DoorwayCleanup cleanup){
|
||||
//doorwayCleanupList.Add(cleanup);
|
||||
}
|
||||
|
||||
public static void AddDunGenScriptHook(IDunGenScriptingParent script){
|
||||
scriptingLists[script.GetScriptingHook].Add(script);
|
||||
}
|
||||
|
27
DunGenPlus/DunGenPlus/Patches/DoorwayPairFinderPatch.cs
Normal file
27
DunGenPlus/DunGenPlus/Patches/DoorwayPairFinderPatch.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using DunGen;
|
||||
using DunGenPlus.Generation;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DunGenPlus.Patches {
|
||||
|
||||
internal class DoorwayPairFinderPatch {
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DoorwayPairFinder), "GetPotentialDoorwayPairsForNonFirstTile")]
|
||||
public static void GenerateBranchPathsPatch(ref DoorwayPairFinder __instance, ref IEnumerable<DoorwayPair> __result){
|
||||
if (DunGenPlusGenerator.Active) {
|
||||
__result = DunGenPlusGenerator.GetPotentialDoorwayPairsForNonFirstTileAlternate(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ namespace DunGenPlus.Patches {
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "InnerGenerate")]
|
||||
public static void InnerGeneratePatch(ref DungeonGenerator __instance, bool isRetry, ref IEnumerator __result){
|
||||
public static void InnerGeneratePatch(ref DungeonGenerator __instance, bool isRetry){
|
||||
//Plugin.logger.LogWarning($"InnerGenerate: {DunGenPlusGenerator.Active}, {DunGenPlusGenerator.ActiveAlternative}, {__instance.Status}");
|
||||
if (API.IsDevDebugModeActive() && !isRetry) {
|
||||
DevDebugManager.Instance.RecordNewSeed(__instance.ChosenSeed);
|
||||
@ -55,7 +55,7 @@ namespace DunGenPlus.Patches {
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(DungeonGenerator), "GenerateMainPath")]
|
||||
public static void GenerateMainPathPatch(ref DungeonGenerator __instance, ref IEnumerator __result){
|
||||
public static void GenerateMainPathPatch(ref DungeonGenerator __instance){
|
||||
//Plugin.logger.LogWarning($"GenerateMainPath: {DunGenPlusGenerator.Active}, {DunGenPlusGenerator.ActiveAlternative}, {__instance.Status}");
|
||||
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
|
||||
DunGenPlusGenerator.RandomizeLineArchetypes(__instance, true);
|
||||
@ -69,7 +69,6 @@ namespace DunGenPlus.Patches {
|
||||
if (DunGenPlusGenerator.Active && DunGenPlusGenerator.ActiveAlternative) {
|
||||
__result = DunGenPlusGenerator.GenerateAlternativeMainPaths(__instance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[HarmonyTranspiler]
|
||||
|
@ -35,7 +35,7 @@ namespace DunGenPlus.Patches {
|
||||
[HarmonyPatch(typeof(TileProxy), MethodType.Constructor, new Type[] { typeof(TileProxy) })]
|
||||
[HarmonyPostfix]
|
||||
public static void TileProxyConstructorExistingPatch(ref TileProxy __instance, TileProxy existingTile){
|
||||
AddTileExtenderProxy(__instance, new TileExtenderProxy(GetTileExtenderProxy(existingTile)));
|
||||
AddTileExtenderProxy(__instance, new TileExtenderProxy(__instance, GetTileExtenderProxy(existingTile)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace DunGenPlus {
|
||||
|
||||
internal const string modGUID = "dev.ladyalice.dungenplus";
|
||||
private const string modName = "Dungeon Generation Plus";
|
||||
private const string modVersion = "1.4.0";
|
||||
private const string modVersion = "1.4.1";
|
||||
|
||||
internal readonly Harmony Harmony = new Harmony(modGUID);
|
||||
|
||||
@ -51,6 +51,7 @@ namespace DunGenPlus {
|
||||
Harmony.PatchAll(typeof(RoundManagerPatch));
|
||||
Harmony.PatchAll(typeof(BranchCountHelperPatch));
|
||||
Harmony.PatchAll(typeof(TileProxyPatch));
|
||||
Harmony.PatchAll(typeof(DoorwayPairFinderPatch));
|
||||
|
||||
try {
|
||||
Harmony.PatchAll(typeof(LethalLevelLoaderPatches));
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user