Skip to content

Commit

Permalink
Update 0.2.0, Basic interactable creation and enum drawer.
Browse files Browse the repository at this point in the history
  • Loading branch information
Nebby1999 committed Nov 26, 2021
1 parent 816452b commit 00e8761
Show file tree
Hide file tree
Showing 8 changed files with 322 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*using RoR2EditorKit.Settings;
using RoR2EditorKit.Settings;
using System.Linq;
using UnityEditor;
using UnityEngine;
Expand Down Expand Up @@ -58,5 +58,4 @@ protected void TryToClose()
}
}
}
}
*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using RoR2;
using UnityEditor;
using UnityEngine;

namespace RoR2EditorKit.RoR2.PropertyDrawers
{
[CustomPropertyDrawer(typeof(EnumMaskAttribute))]
public class EnumMaskDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Enum targetEnum = GetBaseProperty<Enum>(property);

string propName = property.name;
if (string.IsNullOrEmpty(propName))
propName = Regex.Replace(property.name, "([^^])([A-Z])", "$1 $2");
EditorGUI.BeginChangeCheck();
EditorGUI.BeginProperty(position, label, property);

Enum enumNew = EditorGUI.EnumFlagsField(position, ObjectNames.NicifyVariableName(propName), targetEnum);

EditorGUI.EndProperty();
if (EditorGUI.EndChangeCheck())
{
var convertedType = Convert.ChangeType(enumNew, targetEnum.GetType());
property.intValue = Convert.ToInt32(convertedType);
property.serializedObject.ApplyModifiedProperties();
property.serializedObject.UpdateIfRequiredOrScript();
}
}

static T GetBaseProperty<T>(SerializedProperty prop)
{
// Separate the steps it takes to get to this property
string[] separatedPaths = prop.propertyPath.Split('.');

// Go down to the root of this serialized property
System.Object reflectionTarget = prop.serializedObject.targetObject as object;
// Walk down the path to get the target object
foreach (var path in separatedPaths)
{
FieldInfo fieldInfo = reflectionTarget.GetType().GetField(path);
reflectionTarget = fieldInfo.GetValue(reflectionTarget);
}
return (T)reflectionTarget;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
using EntityStates;
using RoR2;
using RoR2EditorKit.Common;
using RoR2EditorKit.Core;
using RoR2EditorKit.Core.Windows;
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;

namespace RoR2EditorKit.RoR2.EditorWindows
{
public class CreateBasicInteractable : CreateRoR2PrefabWindow<EntityStateMachine>
{

private bool hasCost;
private CostTypeIndex costType;
private int cost;
private bool isShrine;

private bool isChest;
private bool summonsSomethingOnPurchased;
private bool isPortal;

private bool createMatchingInteractableSpawnCard;


[MenuItem(Constants.RoR2EditorKitContextRoot + "Prefabs/Interactable", false, Constants.RoR2EditorKitContextPriority)]
public static void Open()
{
OpenEditorWindow<CreateBasicInteractable>(null, "Create Basic Interactable");
}

protected override void OnWindowOpened()
{
base.OnWindowOpened();

hasCost = false;
costType = CostTypeIndex.None;
cost = 0;
isShrine = false;
isChest = false;
summonsSomethingOnPurchased = false;
isPortal = false;
createMatchingInteractableSpawnCard = false;

//Destroying uneeded components from the main prefab
DestroyImmediate(mainPrefab.GetComponent<MeshFilter>());
DestroyImmediate(mainPrefab.GetComponent<MeshRenderer>());
DestroyImmediate(mainPrefab.GetComponent<CapsuleCollider>());

//Adding networking
mainPrefab.AddComponent<NetworkIdentity>();
mainPrefab.AddComponent<NetworkTransform>();
var networkStateMachine = mainPrefab.AddComponent<NetworkStateMachine>();
var type = networkStateMachine.GetType();
var field = type.GetField("stateMachines", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if(field != null)
field.SetValue(networkStateMachine, new EntityStateMachine[] { MainComponent });

//Adding mdl base and the mdl children
var baseAndChild = AddMdlChild();
var mdlLocator = mainPrefab.AddComponent<ModelLocator>();
mdlLocator.modelBaseTransform = baseAndChild.Item1.transform;
mdlLocator.modelTransform = baseAndChild.Item2.transform;

//adding highlight component
var highlight = mainPrefab.AddComponent<Highlight>();
highlight.targetRenderer = baseAndChild.Item2.GetComponent<MeshRenderer>();
highlight.strength = 1;
highlight.highlightColor = Highlight.HighlightColor.interactive;

//Display Provider
var displayProvider = mainPrefab.AddComponent<GenericDisplayNameProvider>();
displayProvider.displayToken = "MYINTERACTABLE_INTERACTABLE_NAME";

//Sound Locator
mainPrefab.AddComponent<SfxLocator>();

mainSerializedObject = new SerializedObject(mainPrefab);
}

private (GameObject, GameObject) AddMdlChild()
{
var modelBase = new GameObject("ModelBase");
modelBase.transform.SetParent(mainPrefab.transform);

var primitive = GameObject.CreatePrimitive(PrimitiveType.Capsule);
primitive.transform.SetParent(modelBase.transform);

var eLocator = primitive.AddComponent<EntityLocator>();
eLocator.entity = mainPrefab;

primitive.AddComponent<ChildLocator>();

return (modelBase, primitive);
}

private void OnGUI()
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical("box");

nameField = EditorGUILayout.TextField(new GUIContent("Interactable Name", "The name of this interactable, will be used on token creation."), nameField);
hasCost = EditorGUILayout.Toggle(new GUIContent("Has Cost", "Wether or not this interactable costs something, enabling this enables extra settings."), hasCost);
if(hasCost)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical("box");
HandleCost();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
isChest = EditorGUILayout.Toggle(new GUIContent("Is Chest", "Wether or not this interactable is a chest."), isChest);
summonsSomethingOnPurchased = EditorGUILayout.Toggle(new GUIContent("Summons something on Interaction", "Wether or not this interactable summons something on interaction, the summoned object needs to be a Master prefab."), summonsSomethingOnPurchased);
isPortal = EditorGUILayout.Toggle(new GUIContent("Is Portal", "Wether or not this interactable is a portal that'll send the player to a new stage."), isPortal);

createMatchingInteractableSpawnCard = EditorGUILayout.Toggle(new GUIContent("Create Matching ISC", "Wether or not a matching interactable spawn card will be created for this interactable."), createMatchingInteractableSpawnCard);

if(SimpleButton("Create Interactable"))
{
var result = CreateInteractable();
if(result)
{
Debug.Log($"Succesfully Created Interactable {nameField}");
TryToClose();
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}

private void HandleCost()
{
costType = (CostTypeIndex)EditorGUILayout.EnumPopup(new GUIContent("Cost Type", "The type of cost this interactable has"), costType);
cost = EditorGUILayout.IntField(new GUIContent("Cost", "How much does this interactable Cost to Interact."), cost);
isShrine = EditorGUILayout.Toggle(new GUIContent("Is Shrine", "Wether or not this interactable qualifies as a Shrine"), isShrine);
}

private bool CreateInteractable()
{
actualName = GetCorrectAssetName(nameField);
try
{
if (string.IsNullOrEmpty(actualName))
throw ErrorShorthands.ThrowNullAssetName(nameof(nameField));

mainPrefab.name = actualName;

if (string.IsNullOrEmpty(Settings.TokenPrefix))
throw ErrorShorthands.ThrowNullTokenPrefix();

MainComponent.customName = actualName;
MainComponent.initialStateType = default(SerializableEntityStateType);
MainComponent.mainStateType = default(SerializableEntityStateType);

mainPrefab.GetComponent<GenericDisplayNameProvider>().displayToken = isShrine ? CreateDisplayToken(true) : CreateDisplayToken(false);

if (hasCost) AddCostComponent();
if (isChest) mainPrefab.AddComponent<ChestBehavior>();
if (summonsSomethingOnPurchased) mainPrefab.AddComponent<SummonMasterBehavior>();
if (isPortal) AddSceneExitController();

var prefab = Util.CreatePrefabAtSelectionPath(mainPrefab);
if (createMatchingInteractableSpawnCard)
CreateISC(prefab);

return true;
}
catch (Exception e)
{
Debug.LogError($"Error while creating Interactable: {e}");
return false;
}
}

private string CreateDisplayToken(bool shrine)
{
if (shrine)
return $"{Settings.TokenPrefix}_SHRINE_{actualName.ToUpperInvariant()}_NAME";
else
return $"{Settings.TokenPrefix}_INTERACTABLE_{actualName.ToUpperInvariant()}_NAME";
}

private void AddCostComponent()
{
var pInteraction = mainPrefab.AddComponent<PurchaseInteraction>();
pInteraction.displayNameToken = $"{Settings.TokenPrefix}_{actualName.ToUpperInvariant()}_NAME";
pInteraction.contextToken = $"{Settings.TokenPrefix}_{actualName.ToUpperInvariant()}_NAME";
pInteraction.costType = costType;
pInteraction.cost = cost;
pInteraction.isShrine = isShrine;
}

private void AddSceneExitController()
{
var genericInteraction = mainPrefab.AddComponent<GenericInteraction>();
genericInteraction.contextToken = $"{Settings.TokenPrefix}_PORTAL_{actualName.ToUpperInvariant()}_NAME";
mainPrefab.AddComponent<SceneExitController>();
}

private void CreateISC(GameObject cardPrefab)
{
var isc = ScriptableObject.CreateInstance<InteractableSpawnCard>();
isc.name = $"isc{actualName}";
isc.prefab = cardPrefab;

Util.CreateAssetAtSelectionPath(isc);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions RoR2EditorKit/Assets/RoR2EditorKit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Features:

RoR2EditorKit comes bundled with custom Inspectors that overwrite the default view of certain Scriptable Objects in RoR2, specifically annoying to work with ones, either with new easier to use inspectors, or editor windows that break down the default inspector for a more viewable experience. Examples of these include:

* Serializable Content Pack: Simply click one of the buttons and the inspector will show it's corresponding list. allowing for easy managment of the content of your mod
* Serializable Content Pack: Simply click one of the buttons and the inspector will show it's corresponding list. allowing for easy managment of the content of your mod, alongside the ability to auto populate these fields
![](https://i.gyazo.com/7d9a746fe9386cfe68f1c1a0d2a44c78.png)

* Entity State Configuration: Easily select an entity state from the target type, when selected, the inspector will automatically populate the serialized fields array with the necesary fields to serialize.
Expand All @@ -25,7 +25,7 @@ RoR2EditorKit comes with custom property drawers for handling certain types insi

## Asset Creator Windows

RoR2EditorKit comes with special editor windows designed specifically for creating Assets for Risk of Rain 2, so far it only comes bundled with editor windows for creating scriptable objects. but we plan on adding more and even complex ones for creating projectiles, or maybe even full body boilerplates.
RoR2EditorKit comes with special editor windows designed specifically for creating Assets for Risk of Rain 2, so far it only comes bundled with editor windows for creating scriptable objects and an Interactable prefab. but we plan on adding more and even complex ones for creating projectiles, or maybe even full body boilerplates.

* ItemDef: Easily create an item def by only giving the name, tier, and tags. you can also automatically create pickup and display prefabs with the correct needed components and proper naming scheme of HopooGames. You can specify more things by clicking the extra settings or prefab settings buttons.

Expand All @@ -49,6 +49,14 @@ RoR2EditorKit comes with special editor windows designed specifically for creati

## Changelog

### 0.2.0

* Added CreateRoR2PrefabWindow, used for creating prefabs.
* Added a window for creating an Interactable prefab.
* Fixed an issue where the Serializable System Type Drawer wouldn't work properly if the inspected type had mode than 1 field.
* Added a fallback on the Serializable System Type Drawer
* Added a property drawer for EnumMasks, allowing proper usage of Flags on RoR2 Enums with the Flags attribute.

### 0.1.4

* Separated the Enabled and Disabled inspector settings to its own setting file. allowing projects to git ignore it.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2f9af1c6015733a42be940cc462aa7ed, type: 3}
m_Name: Log 7
m_EditorClassIdentifier:
pipeline: {fileID: 11400000, guid: 95c42be21b50898489b6e40d35ab0046, type: 2}
creationDate:
ticks: 637735368829041014
entries: []

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 00e8761

Please sign in to comment.