Skip to content

Commit 00e8761

Browse files
committed
Update 0.2.0, Basic interactable creation and enum drawer.
1 parent 816452b commit 00e8761

File tree

8 files changed

+322
-5
lines changed

8 files changed

+322
-5
lines changed

RoR2EditorKit/Assets/RoR2EditorKit/Editor/Core/Windows/CreateRoR2PrefabWindow.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*using RoR2EditorKit.Settings;
1+
using RoR2EditorKit.Settings;
22
using System.Linq;
33
using UnityEditor;
44
using UnityEngine;
@@ -58,5 +58,4 @@ protected void TryToClose()
5858
}
5959
}
6060
}
61-
}
62-
*/
61+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Reflection;
3+
using System.Text.RegularExpressions;
4+
using RoR2;
5+
using UnityEditor;
6+
using UnityEngine;
7+
8+
namespace RoR2EditorKit.RoR2.PropertyDrawers
9+
{
10+
[CustomPropertyDrawer(typeof(EnumMaskAttribute))]
11+
public class EnumMaskDrawer : PropertyDrawer
12+
{
13+
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
14+
{
15+
Enum targetEnum = GetBaseProperty<Enum>(property);
16+
17+
string propName = property.name;
18+
if (string.IsNullOrEmpty(propName))
19+
propName = Regex.Replace(property.name, "([^^])([A-Z])", "$1 $2");
20+
EditorGUI.BeginChangeCheck();
21+
EditorGUI.BeginProperty(position, label, property);
22+
23+
Enum enumNew = EditorGUI.EnumFlagsField(position, ObjectNames.NicifyVariableName(propName), targetEnum);
24+
25+
EditorGUI.EndProperty();
26+
if (EditorGUI.EndChangeCheck())
27+
{
28+
var convertedType = Convert.ChangeType(enumNew, targetEnum.GetType());
29+
property.intValue = Convert.ToInt32(convertedType);
30+
property.serializedObject.ApplyModifiedProperties();
31+
property.serializedObject.UpdateIfRequiredOrScript();
32+
}
33+
}
34+
35+
static T GetBaseProperty<T>(SerializedProperty prop)
36+
{
37+
// Separate the steps it takes to get to this property
38+
string[] separatedPaths = prop.propertyPath.Split('.');
39+
40+
// Go down to the root of this serialized property
41+
System.Object reflectionTarget = prop.serializedObject.targetObject as object;
42+
// Walk down the path to get the target object
43+
foreach (var path in separatedPaths)
44+
{
45+
FieldInfo fieldInfo = reflectionTarget.GetType().GetField(path);
46+
reflectionTarget = fieldInfo.GetValue(reflectionTarget);
47+
}
48+
return (T)reflectionTarget;
49+
}
50+
}
51+
}

RoR2EditorKit/Assets/RoR2EditorKit/Editor/ScriptsForRoR2/PropertyDrawers/EnumMaskDrawer.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
using EntityStates;
2+
using RoR2;
3+
using RoR2EditorKit.Common;
4+
using RoR2EditorKit.Core;
5+
using RoR2EditorKit.Core.Windows;
6+
using System;
7+
using UnityEditor;
8+
using UnityEngine;
9+
using UnityEngine.Networking;
10+
11+
namespace RoR2EditorKit.RoR2.EditorWindows
12+
{
13+
public class CreateBasicInteractable : CreateRoR2PrefabWindow<EntityStateMachine>
14+
{
15+
16+
private bool hasCost;
17+
private CostTypeIndex costType;
18+
private int cost;
19+
private bool isShrine;
20+
21+
private bool isChest;
22+
private bool summonsSomethingOnPurchased;
23+
private bool isPortal;
24+
25+
private bool createMatchingInteractableSpawnCard;
26+
27+
28+
[MenuItem(Constants.RoR2EditorKitContextRoot + "Prefabs/Interactable", false, Constants.RoR2EditorKitContextPriority)]
29+
public static void Open()
30+
{
31+
OpenEditorWindow<CreateBasicInteractable>(null, "Create Basic Interactable");
32+
}
33+
34+
protected override void OnWindowOpened()
35+
{
36+
base.OnWindowOpened();
37+
38+
hasCost = false;
39+
costType = CostTypeIndex.None;
40+
cost = 0;
41+
isShrine = false;
42+
isChest = false;
43+
summonsSomethingOnPurchased = false;
44+
isPortal = false;
45+
createMatchingInteractableSpawnCard = false;
46+
47+
//Destroying uneeded components from the main prefab
48+
DestroyImmediate(mainPrefab.GetComponent<MeshFilter>());
49+
DestroyImmediate(mainPrefab.GetComponent<MeshRenderer>());
50+
DestroyImmediate(mainPrefab.GetComponent<CapsuleCollider>());
51+
52+
//Adding networking
53+
mainPrefab.AddComponent<NetworkIdentity>();
54+
mainPrefab.AddComponent<NetworkTransform>();
55+
var networkStateMachine = mainPrefab.AddComponent<NetworkStateMachine>();
56+
var type = networkStateMachine.GetType();
57+
var field = type.GetField("stateMachines", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
58+
if(field != null)
59+
field.SetValue(networkStateMachine, new EntityStateMachine[] { MainComponent });
60+
61+
//Adding mdl base and the mdl children
62+
var baseAndChild = AddMdlChild();
63+
var mdlLocator = mainPrefab.AddComponent<ModelLocator>();
64+
mdlLocator.modelBaseTransform = baseAndChild.Item1.transform;
65+
mdlLocator.modelTransform = baseAndChild.Item2.transform;
66+
67+
//adding highlight component
68+
var highlight = mainPrefab.AddComponent<Highlight>();
69+
highlight.targetRenderer = baseAndChild.Item2.GetComponent<MeshRenderer>();
70+
highlight.strength = 1;
71+
highlight.highlightColor = Highlight.HighlightColor.interactive;
72+
73+
//Display Provider
74+
var displayProvider = mainPrefab.AddComponent<GenericDisplayNameProvider>();
75+
displayProvider.displayToken = "MYINTERACTABLE_INTERACTABLE_NAME";
76+
77+
//Sound Locator
78+
mainPrefab.AddComponent<SfxLocator>();
79+
80+
mainSerializedObject = new SerializedObject(mainPrefab);
81+
}
82+
83+
private (GameObject, GameObject) AddMdlChild()
84+
{
85+
var modelBase = new GameObject("ModelBase");
86+
modelBase.transform.SetParent(mainPrefab.transform);
87+
88+
var primitive = GameObject.CreatePrimitive(PrimitiveType.Capsule);
89+
primitive.transform.SetParent(modelBase.transform);
90+
91+
var eLocator = primitive.AddComponent<EntityLocator>();
92+
eLocator.entity = mainPrefab;
93+
94+
primitive.AddComponent<ChildLocator>();
95+
96+
return (modelBase, primitive);
97+
}
98+
99+
private void OnGUI()
100+
{
101+
EditorGUILayout.BeginHorizontal();
102+
EditorGUILayout.BeginVertical("box");
103+
104+
nameField = EditorGUILayout.TextField(new GUIContent("Interactable Name", "The name of this interactable, will be used on token creation."), nameField);
105+
hasCost = EditorGUILayout.Toggle(new GUIContent("Has Cost", "Wether or not this interactable costs something, enabling this enables extra settings."), hasCost);
106+
if(hasCost)
107+
{
108+
EditorGUILayout.BeginHorizontal();
109+
EditorGUILayout.BeginVertical("box");
110+
HandleCost();
111+
EditorGUILayout.EndVertical();
112+
EditorGUILayout.EndHorizontal();
113+
}
114+
isChest = EditorGUILayout.Toggle(new GUIContent("Is Chest", "Wether or not this interactable is a chest."), isChest);
115+
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);
116+
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);
117+
118+
createMatchingInteractableSpawnCard = EditorGUILayout.Toggle(new GUIContent("Create Matching ISC", "Wether or not a matching interactable spawn card will be created for this interactable."), createMatchingInteractableSpawnCard);
119+
120+
if(SimpleButton("Create Interactable"))
121+
{
122+
var result = CreateInteractable();
123+
if(result)
124+
{
125+
Debug.Log($"Succesfully Created Interactable {nameField}");
126+
TryToClose();
127+
}
128+
}
129+
EditorGUILayout.EndVertical();
130+
EditorGUILayout.EndHorizontal();
131+
}
132+
133+
private void HandleCost()
134+
{
135+
costType = (CostTypeIndex)EditorGUILayout.EnumPopup(new GUIContent("Cost Type", "The type of cost this interactable has"), costType);
136+
cost = EditorGUILayout.IntField(new GUIContent("Cost", "How much does this interactable Cost to Interact."), cost);
137+
isShrine = EditorGUILayout.Toggle(new GUIContent("Is Shrine", "Wether or not this interactable qualifies as a Shrine"), isShrine);
138+
}
139+
140+
private bool CreateInteractable()
141+
{
142+
actualName = GetCorrectAssetName(nameField);
143+
try
144+
{
145+
if (string.IsNullOrEmpty(actualName))
146+
throw ErrorShorthands.ThrowNullAssetName(nameof(nameField));
147+
148+
mainPrefab.name = actualName;
149+
150+
if (string.IsNullOrEmpty(Settings.TokenPrefix))
151+
throw ErrorShorthands.ThrowNullTokenPrefix();
152+
153+
MainComponent.customName = actualName;
154+
MainComponent.initialStateType = default(SerializableEntityStateType);
155+
MainComponent.mainStateType = default(SerializableEntityStateType);
156+
157+
mainPrefab.GetComponent<GenericDisplayNameProvider>().displayToken = isShrine ? CreateDisplayToken(true) : CreateDisplayToken(false);
158+
159+
if (hasCost) AddCostComponent();
160+
if (isChest) mainPrefab.AddComponent<ChestBehavior>();
161+
if (summonsSomethingOnPurchased) mainPrefab.AddComponent<SummonMasterBehavior>();
162+
if (isPortal) AddSceneExitController();
163+
164+
var prefab = Util.CreatePrefabAtSelectionPath(mainPrefab);
165+
if (createMatchingInteractableSpawnCard)
166+
CreateISC(prefab);
167+
168+
return true;
169+
}
170+
catch (Exception e)
171+
{
172+
Debug.LogError($"Error while creating Interactable: {e}");
173+
return false;
174+
}
175+
}
176+
177+
private string CreateDisplayToken(bool shrine)
178+
{
179+
if (shrine)
180+
return $"{Settings.TokenPrefix}_SHRINE_{actualName.ToUpperInvariant()}_NAME";
181+
else
182+
return $"{Settings.TokenPrefix}_INTERACTABLE_{actualName.ToUpperInvariant()}_NAME";
183+
}
184+
185+
private void AddCostComponent()
186+
{
187+
var pInteraction = mainPrefab.AddComponent<PurchaseInteraction>();
188+
pInteraction.displayNameToken = $"{Settings.TokenPrefix}_{actualName.ToUpperInvariant()}_NAME";
189+
pInteraction.contextToken = $"{Settings.TokenPrefix}_{actualName.ToUpperInvariant()}_NAME";
190+
pInteraction.costType = costType;
191+
pInteraction.cost = cost;
192+
pInteraction.isShrine = isShrine;
193+
}
194+
195+
private void AddSceneExitController()
196+
{
197+
var genericInteraction = mainPrefab.AddComponent<GenericInteraction>();
198+
genericInteraction.contextToken = $"{Settings.TokenPrefix}_PORTAL_{actualName.ToUpperInvariant()}_NAME";
199+
mainPrefab.AddComponent<SceneExitController>();
200+
}
201+
202+
private void CreateISC(GameObject cardPrefab)
203+
{
204+
var isc = ScriptableObject.CreateInstance<InteractableSpawnCard>();
205+
isc.name = $"isc{actualName}";
206+
isc.prefab = cardPrefab;
207+
208+
Util.CreateAssetAtSelectionPath(isc);
209+
}
210+
}
211+
}

RoR2EditorKit/Assets/RoR2EditorKit/Editor/ScriptsForRoR2/Windows/AssetCreators/Prefabs/CreateBasicInteractable.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

RoR2EditorKit/Assets/RoR2EditorKit/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Features:
1010

1111
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:
1212

13-
* 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
13+
* 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
1414
![](https://i.gyazo.com/7d9a746fe9386cfe68f1c1a0d2a44c78.png)
1515

1616
* 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.
@@ -25,7 +25,7 @@ RoR2EditorKit comes with custom property drawers for handling certain types insi
2525

2626
## Asset Creator Windows
2727

28-
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.
28+
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.
2929

3030
* 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.
3131

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

5050
## Changelog
5151

52+
### 0.2.0
53+
54+
* Added CreateRoR2PrefabWindow, used for creating prefabs.
55+
* Added a window for creating an Interactable prefab.
56+
* Fixed an issue where the Serializable System Type Drawer wouldn't work properly if the inspected type had mode than 1 field.
57+
* Added a fallback on the Serializable System Type Drawer
58+
* Added a property drawer for EnumMasks, allowing proper usage of Flags on RoR2 Enums with the Flags attribute.
59+
5260
### 0.1.4
5361

5462
* Separated the Enabled and Disabled inspector settings to its own setting file. allowing projects to git ignore it.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &11400000
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 0
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: 2f9af1c6015733a42be940cc462aa7ed, type: 3}
13+
m_Name: Log 7
14+
m_EditorClassIdentifier:
15+
pipeline: {fileID: 11400000, guid: 95c42be21b50898489b6e40d35ab0046, type: 2}
16+
creationDate:
17+
ticks: 637735368829041014
18+
entries: []

RoR2EditorKit/Assets/ThunderKitSettings/Logs/PublishToZip/Log 7.asset.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)