New enemy, the maid

This commit is contained in:
LadyAliceMargatroid 2024-06-26 08:15:58 -07:00
parent 37e3d6e9b1
commit 05ea3dc4c3
8 changed files with 1481 additions and 63 deletions

View File

@ -63,6 +63,7 @@ namespace ScarletMansion {
} }
public static Enemy knight; public static Enemy knight;
public static Enemy maid;
// item values // item values
@ -167,12 +168,18 @@ namespace ScarletMansion {
public static Sprite hoverIcon; public static Sprite hoverIcon;
private static string GetAssemblyName() => Assembly.GetExecutingAssembly().FullName.Split(',')[0];
public static void LoadAssetBundle() { public static void LoadAssetBundle() {
if (MainAssetBundle == null) { if (MainAssetBundle == null) {
using (var assetStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetAssemblyName() + "." + mainAssetBundleName)) { var assembly = Assembly.GetExecutingAssembly();
MainAssetBundle = AssetBundle.LoadFromStream(assetStream); var resourceNames = assembly.GetManifestResourceNames();
if (resourceNames.Length >= 1) {
var name = resourceNames[0];
using (var assetStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) {
Plugin.logger.LogInfo($"Loading resource {name}");
MainAssetBundle = AssetBundle.LoadFromStream(assetStream);
}
} }
} }
dungeon = Load<DungeonFlow>("SDMLevel"); dungeon = Load<DungeonFlow>("SDMLevel");
@ -185,6 +192,12 @@ namespace ScarletMansion {
Load<TerminalKeyword>("KnightKeyword") Load<TerminalKeyword>("KnightKeyword")
); );
maid = new Enemy(
Load<GameObject>("NET_MaidEnemy"),
Load<TerminalNode>("MaidNode"),
Load<TerminalKeyword>("MaidKeyword")
);
RegisterNetworkPrefab(networkObjectList.networkDungeon); RegisterNetworkPrefab(networkObjectList.networkDungeon);
RegisterNetworkPrefab(networkObjectList.networkDoors); RegisterNetworkPrefab(networkObjectList.networkDoors);
RegisterNetworkPrefab(networkObjectList.networkItems); RegisterNetworkPrefab(networkObjectList.networkItems);

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,8 @@ using LethalLevelLoader;
using static UnityEngine.GraphicsBuffer; using static UnityEngine.GraphicsBuffer;
using ScarletMansion.GamePatch.Components; using ScarletMansion.GamePatch.Components;
using System.Security.Cryptography; using System.Security.Cryptography;
using ScarletMansion.GamePatch.Enemies;
using UnityEngine.UI;
namespace ScarletMansion.GamePatch { namespace ScarletMansion.GamePatch {
@ -130,11 +132,12 @@ namespace ScarletMansion.GamePatch {
} }
} }
var allEnemies = round.levels
.SelectMany(lev => lev.Enemies);
var knight = Assets.knight; var knight = Assets.knight;
if (knight.enemyType == null){ if (knight.enemyType == null){
var springItem = round.levels var springItem = allEnemies.FirstOrDefault(e => e.enemyType.name.ToLowerInvariant() == "springman");
.SelectMany(lev => lev.Enemies)
.FirstOrDefault(e => e.enemyType.name.ToLowerInvariant() == "springman");
if (GameReadNullCheck(springItem, "springman", "Knight enemy will not spawn")) { if (GameReadNullCheck(springItem, "springman", "Knight enemy will not spawn")) {
var type = ScriptableObject.Instantiate(springItem.enemyType); var type = ScriptableObject.Instantiate(springItem.enemyType);
@ -143,11 +146,44 @@ namespace ScarletMansion.GamePatch {
type.enemyName = "Knight"; type.enemyName = "Knight";
knight.enemyType = type; knight.enemyType = type;
knight.enemy.GetComponentInChildren<KnightVariant>().enemyType = type; knight.enemy.GetComponentInChildren<EnemyAI>().enemyType = type;
Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, knight.terminalNode, knight.terminalKeyword); LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, knight.terminalNode, knight.terminalKeyword);
} }
} }
var maid = Assets.maid;
if (maid.enemyType == null){
var butlerItem = allEnemies.FirstOrDefault(e => e.enemyType.name.ToLowerInvariant() == "butler");
if (GameReadNullCheck(butlerItem, "butler", "Maid enemy will not spawn")) {
var type = ScriptableObject.Instantiate(butlerItem.enemyType);
type.name = "Maid";
type.enemyPrefab = maid.enemy;
type.enemyName = "Maid";
type.pushPlayerForce *= 0.25f;
maid.enemyType = type;
var maidScript = maid.enemy.GetComponentInChildren<MaidVariant>();
maidScript.enemyType = type;
var butlerPrefab = butlerItem.enemyType.enemyPrefab;
var butlerScript = butlerPrefab.GetComponent<ButlerEnemyAI>();
var butlerBloodStabParticle = Utility.FindChildRecurvisely(butlerPrefab.transform, "BloodStabParticle");
var butlerBloodParticle = Utility.FindChildRecurvisely(butlerPrefab.transform, "BloodParticle");
if (GameReadNullCheck(butlerBloodStabParticle, "BloodStabParticle", "Messed up errors will probably happen with blood splats") && GameReadNullCheck(butlerBloodParticle, "BloodParticle", "Messed up errors will probably happen with blood splats")){
var ps1 = maidScript.stabBloodParticle.GetComponent<ParticleSystemRenderer>();
ps1.material = butlerBloodStabParticle.GetComponent<ParticleSystemRenderer>().material;
var ps2 = ps1.transform.GetChild(0).GetComponent<ParticleSystemRenderer>();
ps2.material = butlerBloodParticle.GetComponent<ParticleSystemRenderer>().material;
}
maidScript.knifePrefab = butlerScript.knifePrefab;
LethalLib.Modules.Enemies.RegisterEnemy(type, 0, Levels.LevelTypes.None, maid.terminalNode, maid.terminalKeyword);
}
}
} catch (Exception e) { } catch (Exception e) {

View File

@ -101,41 +101,41 @@ namespace ScarletMansion.GamePatch {
currentEnemiesRarity = lastEnemiesRarity.ToList(); currentEnemiesRarity = lastEnemiesRarity.ToList();
currentItemsRarity = lastItemsRarity.ToList(); currentItemsRarity = lastItemsRarity.ToList();
if (Assets.knight != null) { void AddEnemy(Assets.Enemy enemy, string sourceEnemyName, string targetEnemyName, int baseWeight, int minBaseWeight, float weightStealPercentage) {
var baseWeight = PluginConfig.Instance.knightWeightBaseValue; if (enemy != null) {
var target = currentEnemiesRarity var target = currentEnemiesRarity
.Where(c => c.enemyType.name.ToLowerInvariant() == "springman") .Where(c => c.enemyType.name.ToLowerInvariant() == targetEnemyName)
.FirstOrDefault(); .FirstOrDefault();
if (target == null){ if (target == null){
const int noCoilheaBaseKnightRarity = 10; Plugin.logger.LogInfo($"No enemy {targetEnemyName} in level, using default rarity of {minBaseWeight}.");
Plugin.logger.LogInfo($"No spring enemy in level, using default rarity of {noCoilheaBaseKnightRarity} for knight"); var entry = enemy.GetItemEntry(baseWeight + minBaseWeight);
var knight = Assets.knight.GetItemEntry(noCoilheaBaseKnightRarity + baseWeight); Plugin.logger.LogInfo($"Adding enemy {sourceEnemyName} with weight {entry.rarity}");
currentEnemiesRarity.Add(entry);
Plugin.logger.LogInfo($"Adding enemy Knight with weight {knight.rarity}"); } else {
currentEnemiesRarity.Add(knight); currentEnemiesRarity.Remove(target);
var entryRarity = Mathf.RoundToInt(target.rarity * weightStealPercentage);
var prevEntry = new SpawnableEnemyWithRarity();
prevEntry.enemyType = target.enemyType;
prevEntry.rarity = target.rarity - entryRarity;
var newEntry = enemy.GetItemEntry(baseWeight + entryRarity);
Plugin.logger.LogInfo($"Adding enemy {sourceEnemyName} with weight {newEntry.rarity}");
Plugin.logger.LogInfo($"Setting enemy {targetEnemyName} with weight {prevEntry.rarity}");
currentEnemiesRarity.Add(newEntry);
currentEnemiesRarity.Add(prevEntry);
}
} else { } else {
currentEnemiesRarity.Remove(target); Plugin.logger.LogError($"Failed to load custom enemy {sourceEnemyName} as their reference is missing");
var percentage = PluginConfig.Instance.knightWeightStealPercentageValue;
var knightRarity = Mathf.RoundToInt(target.rarity * percentage);
var spring = new SpawnableEnemyWithRarity();
spring.enemyType = target.enemyType;
spring.rarity = target.rarity - knightRarity;
var knight = Assets.knight.GetItemEntry(knightRarity + baseWeight);
Plugin.logger.LogInfo($"Adding enemy Knight with weight {knight.rarity}");
Plugin.logger.LogInfo($"Setting enemy Coil-head with weight {spring.rarity}");
currentEnemiesRarity.Add(spring);
currentEnemiesRarity.Add(knight);
} }
} else {
Plugin.logger.LogError($"Failed to load custom enemy as their reference is missing");
} }
AddEnemy(Assets.knight, "knight", "springman", PluginConfig.Instance.knightWeightBaseValue, 10, PluginConfig.Instance.knightWeightStealPercentageValue);
AddEnemy(Assets.maid, "maid", "butler", PluginConfig.Instance.maidWeightBaseValue, 10, PluginConfig.Instance.maidWeightStealPercentageValue);
foreach(var i in Assets.scrapItems){ foreach(var i in Assets.scrapItems){
var entry = i.GetItemRarity(); var entry = i.GetItemRarity();

View File

@ -21,10 +21,9 @@ namespace ScarletMansion {
[BepInPlugin(modGUID, modName, modVersion)] [BepInPlugin(modGUID, modName, modVersion)]
[BepInDependency("imabatby.lethallevelloader", "1.2.0.1")] [BepInDependency("imabatby.lethallevelloader", "1.2.0.3")]
[BepInDependency("evaisa.lethallib", "0.13.2")] [BepInDependency("evaisa.lethallib", "0.13.2")]
[BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.advancedCompanyGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.lethalConfigGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInDependency(ModCompability.facilityMeldownGuid, BepInDependency.DependencyFlags.SoftDependency)] [BepInDependency(ModCompability.facilityMeldownGuid, BepInDependency.DependencyFlags.SoftDependency)]
@ -33,7 +32,7 @@ namespace ScarletMansion {
public class Plugin : BaseUnityPlugin { public class Plugin : BaseUnityPlugin {
public const string modGUID = "ImoutoSama.ScarletMansion"; public const string modGUID = "ImoutoSama.ScarletMansion";
private const string modName = "Scarlet Mansion"; private const string modName = "Scarlet Mansion";
private const string modVersion = "1.3.15"; private const string modVersion = "1.3.17";
public readonly Harmony harmony = new Harmony(modGUID); public readonly Harmony harmony = new Harmony(modGUID);
@ -136,7 +135,7 @@ namespace ScarletMansion {
NetworkPrefabs.RegisterNetworkPrefab(i.item.spawnPrefab); NetworkPrefabs.RegisterNetworkPrefab(i.item.spawnPrefab);
} }
extendedDungeon.dungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch); extendedDungeon.DungeonEvents.onBeforeDungeonGenerate.AddListener(GeneratePathPatch.GeneratePatch);
DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction); DoorwayManager.onMainEntranceTeleportSpawnedEvent.AddEvent("DoorwayCleanup", DoorwayManager.onMainEntranceTeleportSpawnedFunction);
} }

View File

@ -26,11 +26,15 @@ namespace ScarletMansion {
public const string dungeonWeightPrefix = "Dungeon Weight"; public const string dungeonWeightPrefix = "Dungeon Weight";
public const string dungeonGenerationPrefix = "Dungeon Generation"; public const string dungeonGenerationPrefix = "Dungeon Generation";
public const string dungeonGenerationBoundingBoxPrefix = "DunGen Bounding Box"; public const string dungeonGenerationBoundingBoxPrefix = "DunGen Bounding Box";
public const string dungeonGenerationMPathsPrefix = "DunGen Main Path"; public const string dungeonGenerationMPathsPrefix = "DunGen Main Path";
public const string dungeonGenerationBPathOnePrefix = "DunGen Branching Path 1"; public const string dungeonGenerationBPathOnePrefix = "DunGen Branching Path 1";
public const string dungeonGenerationBPathTwoPrefix = "DunGen Branching Path 2"; public const string dungeonGenerationBPathTwoPrefix = "DunGen Branching Path 2";
public const string dungeonGenerationBPathThreePrefix = "DunGen Branching Path 3"; public const string dungeonGenerationBPathThreePrefix = "DunGen Branching Path 3";
public const string dungeonLootAndEnemiesPrefix = "Dungeon Loot And Enemies";
public const string dungeonLootPrefix = "Dungeon Loot";
public const string dungeonEnemiesPrefix = "Dungeon Enemies";
public const string dungeonFeaturesPrefix = "Dungeon Features"; public const string dungeonFeaturesPrefix = "Dungeon Features";
public const string dungeonPaintingEventPrefix = "Dungeon Painting Event"; public const string dungeonPaintingEventPrefix = "Dungeon Painting Event";
public const string dungeonLightingPrefix = "Lighting"; public const string dungeonLightingPrefix = "Lighting";
@ -214,7 +218,7 @@ namespace ScarletMansion {
// loot // loot
public static ConfigEntryBundle<float> lootMultiplier = new ConfigEntryBundle<float>( public static ConfigEntryBundle<float> lootMultiplier = new ConfigEntryBundle<float>(
dungeonLootAndEnemiesPrefix, dungeonLootPrefix,
"Loot Multiplier", "Loot Multiplier",
1.4f, 1.4f,
"Multiplies the total amount of loot for the dungeon.", "Multiplies the total amount of loot for the dungeon.",
@ -222,26 +226,8 @@ namespace ScarletMansion {
new AcceptableValueRange<float>(0.25f, 4f) new AcceptableValueRange<float>(0.25f, 4f)
); );
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(
dungeonLootAndEnemiesPrefix,
"Map Hazards Multiplier",
1.6f,
"Multiplies the total amount of map hazards (landmines, turrets) for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundle<int> minIndoorEnemySpawnCount = new ConfigEntryBundle<int>(
dungeonLootAndEnemiesPrefix,
"Minimum Indoor Enemy Spawn Count",
1,
"Increases the minimum amount of indoor enemies that spawn with each spawn wave. For reference, Eclipse is +3.",
null,
new AcceptableValueRange<int>(0, 3)
);
public static ConfigEntryBundle<int> crystalWeight = new ConfigEntryBundle<int>( public static ConfigEntryBundle<int> crystalWeight = new ConfigEntryBundle<int>(
dungeonLootAndEnemiesPrefix, dungeonLootPrefix,
"Decorative Crystal Weight", "Decorative Crystal Weight",
50, 50,
"The decorative crystal's spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.", "The decorative crystal's spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.",
@ -250,7 +236,7 @@ namespace ScarletMansion {
); );
public static ConfigEntryBundle<int> crystalBrokenWeight = new ConfigEntryBundle<int>( public static ConfigEntryBundle<int> crystalBrokenWeight = new ConfigEntryBundle<int>(
dungeonLootAndEnemiesPrefix, dungeonLootPrefix,
"Shattered Decorative Crystal Weight", "Shattered Decorative Crystal Weight",
5, 5,
"The shattered decorative crystal's spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.", "The shattered decorative crystal's spawn weight. Calculating spawn chance (%) is difficult as the total scrap weight for each moon varies from ~600 to ~850.",
@ -258,8 +244,26 @@ namespace ScarletMansion {
new AcceptableValueRange<int>(0, 999) new AcceptableValueRange<int>(0, 999)
); );
public static ConfigEntryBundle<float> mapHazardsMultiplier = new ConfigEntryBundle<float>(
dungeonEnemiesPrefix,
"Map Hazards Multiplier",
1.6f,
"Multiplies the total amount of map hazards (landmines, turrets) for the dungeon.",
null,
new AcceptableValueRange<float>(0.25f, 4f)
);
public static ConfigEntryBundle<int> minIndoorEnemySpawnCount = new ConfigEntryBundle<int>(
dungeonEnemiesPrefix,
"Minimum Indoor Enemy Spawn Count",
1,
"Increases the minimum amount of indoor enemies that spawn with each spawn wave. For reference, Eclipse is +3.",
null,
new AcceptableValueRange<int>(0, 3)
);
public static ConfigEntryBundle<float> knightWeightStealPercentage = new ConfigEntryBundle<float>( public static ConfigEntryBundle<float> knightWeightStealPercentage = new ConfigEntryBundle<float>(
dungeonLootAndEnemiesPrefix, dungeonEnemiesPrefix,
"Knight Weight Steal Percentage", "Knight Weight Steal Percentage",
0.75f, 0.75f,
"The percentage of spawn weight that the knight steals from the coil-head for that moon.\nSetting this 0 means that the coil-head's weight is unaffected and the knight's weight is based entirely by Knight Weight Base.\nSetting this 1 means the knight effectively replaces the coil-head.\nIf the moon doesn't spawn the coil-head, the knight's base weight is set to 10.", "The percentage of spawn weight that the knight steals from the coil-head for that moon.\nSetting this 0 means that the coil-head's weight is unaffected and the knight's weight is based entirely by Knight Weight Base.\nSetting this 1 means the knight effectively replaces the coil-head.\nIf the moon doesn't spawn the coil-head, the knight's base weight is set to 10.",
@ -268,7 +272,7 @@ namespace ScarletMansion {
); );
public static ConfigEntryBundle<int> knightWeightBase = new ConfigEntryBundle<int>( public static ConfigEntryBundle<int> knightWeightBase = new ConfigEntryBundle<int>(
dungeonLootAndEnemiesPrefix, dungeonEnemiesPrefix,
"Knight Weight Base", "Knight Weight Base",
0, 0,
"The base spawn weight of the knight. This is added onto the spawn weight stolen from the coil-head, or the base weight of 10 if the moon doesn't spawn the coil-head.", "The base spawn weight of the knight. This is added onto the spawn weight stolen from the coil-head, or the base weight of 10 if the moon doesn't spawn the coil-head.",
@ -276,15 +280,37 @@ namespace ScarletMansion {
new AcceptableValueRange<int>(0, 999) new AcceptableValueRange<int>(0, 999)
); );
public static ConfigEntryBundle<float> maidWeightStealPercentage = new ConfigEntryBundle<float>(
dungeonEnemiesPrefix,
"Maid Weight Steal Percentage",
0.75f,
"The percentage of spawn weight that the maid steals from the butler for that moon.\nSetting this 0 means that the butler's weight is unaffected and the maid's weight is based entirely by Maid Weight Base.\nSetting this 1 means the maid effectively replaces the butler.\nIf the moon doesn't spawn the bulter, the maid's base weight is set to 10.",
null,
new AcceptableValueRange<float>(0f, 1f)
);
public static ConfigEntryBundle<int> maidWeightBase = new ConfigEntryBundle<int>(
dungeonEnemiesPrefix,
"Maid Weight Base",
0,
"The base spawn weight of the maid. This is added onto the spawn weight stolen from the butler, or the base weight of 10 if the moon doesn't spawn the butler.",
null,
new AcceptableValueRange<int>(0, 999)
);
public float lootMultiplierValue; public float lootMultiplierValue;
public int crystalWeightValue; public int crystalWeightValue;
public int crystalBrokenWeightValue; public int crystalBrokenWeightValue;
public float mapHazardsMultiplierValue; public float mapHazardsMultiplierValue;
public int minIndoorEnemySpawnCountValue; public int minIndoorEnemySpawnCountValue;
public float knightWeightStealPercentageValue; public float knightWeightStealPercentageValue;
public int knightWeightBaseValue; public int knightWeightBaseValue;
public float maidWeightStealPercentageValue;
public int maidWeightBaseValue;
// features // features
public static ConfigEntryBundle<int> shovelDamage = new ConfigEntryBundle<int>( public static ConfigEntryBundle<int> shovelDamage = new ConfigEntryBundle<int>(

View File

@ -83,6 +83,10 @@
<Reference Include="Unity.AI.Navigation"> <Reference Include="Unity.AI.Navigation">
<HintPath>..\..\..\Libraries\Unity.AI.Navigation.dll</HintPath> <HintPath>..\..\..\Libraries\Unity.AI.Navigation.dll</HintPath>
</Reference> </Reference>
<Reference Include="Unity.Animation.Rigging, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Libraries\Unity.Animation.Rigging.dll</HintPath>
</Reference>
<Reference Include="Unity.Collections"> <Reference Include="Unity.Collections">
<HintPath>..\..\..\Libraries\Unity.Collections.dll</HintPath> <HintPath>..\..\..\Libraries\Unity.Collections.dll</HintPath>
</Reference> </Reference>
@ -187,6 +191,7 @@
<Compile Include="GamePatch\Components\ScarletYukariTrigger.cs" /> <Compile Include="GamePatch\Components\ScarletYukariTrigger.cs" />
<Compile Include="GamePatch\DoorLockPatch.cs" /> <Compile Include="GamePatch\DoorLockPatch.cs" />
<Compile Include="GamePatch\Enemies\KnightVariant.cs" /> <Compile Include="GamePatch\Enemies\KnightVariant.cs" />
<Compile Include="GamePatch\Enemies\MaidVariant.cs" />
<Compile Include="GamePatch\EnemyVentPatch.cs" /> <Compile Include="GamePatch\EnemyVentPatch.cs" />
<Compile Include="GamePatch\ExtendedDungeonMapLoad.cs" /> <Compile Include="GamePatch\ExtendedDungeonMapLoad.cs" />
<Compile Include="GamePatch\FixValues\FixBaseClass.cs" /> <Compile Include="GamePatch\FixValues\FixBaseClass.cs" />

View File

@ -179,6 +179,27 @@ namespace ScarletMansion {
return null; return null;
} }
// https://discussions.unity.com/t/how-to-get-a-component-from-an-object-and-add-it-to-another-copy-components-at-runtime/80939/4
public static T GetCopyOf<T>(this Component comp, T other) where T : Component {
Type type = comp.GetType();
if (type != other.GetType()) return null; // type mis-match
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;
PropertyInfo[] pinfos = type.GetProperties(flags);
foreach (var pinfo in pinfos) {
if (pinfo.CanWrite) {
try {
pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
}
catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
}
}
FieldInfo[] finfos = type.GetFields(flags);
foreach (var finfo in finfos) {
finfo.SetValue(comp, finfo.GetValue(other));
}
return comp as T;
}
public static void PrintToParent(Transform t) { public static void PrintToParent(Transform t) {
Plugin.logger.LogInfo(t.name); Plugin.logger.LogInfo(t.name);
var parent = t.parent; var parent = t.parent;