Demon Fixes
This commit is contained in:
@@ -13,7 +13,7 @@ namespace DemonBoss.Magic
|
||||
[Tooltip("Fireball prefab (projectile with its own targeting logic)")]
|
||||
public GameObject fireballPrefab;
|
||||
|
||||
[Tooltip("Seconds between shots")]
|
||||
[Tooltip("Base seconds between shots")]
|
||||
public float fireRate = 0.7f;
|
||||
|
||||
[Tooltip("Maximum number of shots before auto-despawn")]
|
||||
@@ -22,6 +22,16 @@ namespace DemonBoss.Magic
|
||||
[Tooltip("Wait time after last shot before despawn")]
|
||||
public float despawnDelay = 3f;
|
||||
|
||||
[Header("Randomization (Desync)")]
|
||||
[Tooltip("Random initial delay after spawn to desync turrets (seconds)")]
|
||||
public Vector2 initialStaggerRange = new Vector2(0.0f, 0.6f);
|
||||
|
||||
[Tooltip("Per-shot random jitter added to fireRate (seconds). Range x means +/- x.")]
|
||||
public float fireRateJitter = 0.2f;
|
||||
|
||||
[Tooltip("Aim wobble in degrees (0 = disabled). Small value adds natural dispersion.")]
|
||||
public float aimJitterDegrees = 0f;
|
||||
|
||||
[Header("Rotation Configuration")]
|
||||
[Tooltip("Yaw rotation speed in degrees per second")]
|
||||
public float turnSpeed = 120f;
|
||||
@@ -81,102 +91,69 @@ namespace DemonBoss.Magic
|
||||
|
||||
if (muzzle == null)
|
||||
{
|
||||
// Try to find a child named "muzzle"; fallback to self
|
||||
Transform muzzleChild = crystalTransform.Find("muzzle");
|
||||
if (muzzleChild != null)
|
||||
{
|
||||
muzzle = muzzleChild;
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Found muzzle child");
|
||||
}
|
||||
else
|
||||
{
|
||||
muzzle = crystalTransform;
|
||||
if (enableDebug) Debug.LogWarning("[CrystalShooterAI] Using crystal center as muzzle");
|
||||
}
|
||||
muzzle = muzzleChild != null ? muzzleChild : crystalTransform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Crystal activated");
|
||||
|
||||
if (autoFindPlayer && target == null)
|
||||
FindPlayer();
|
||||
|
||||
StartShooting();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update tick: rotate towards target or idle spin.
|
||||
/// </summary>
|
||||
/// <summary> Update tick: rotate towards target or idle spin. </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (!isActive) return;
|
||||
RotateTowardsTarget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find the player by tag (for turret-only aiming).
|
||||
/// </summary>
|
||||
/// <summary> Attempts to find the player by tag (for turret-only aiming). </summary>
|
||||
private void FindPlayer()
|
||||
{
|
||||
GameObject player = GameObject.FindGameObjectWithTag(playerTag);
|
||||
if (player != null)
|
||||
{
|
||||
SetTarget(player.transform);
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Automatically found player");
|
||||
}
|
||||
else if (enableDebug)
|
||||
{
|
||||
Debug.LogWarning("[CrystalShooterAI] Cannot find player with tag: " + playerTag);
|
||||
}
|
||||
if (player != null) SetTarget(player.transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the turret's aiming target (does NOT propagate to projectiles).
|
||||
/// </summary>
|
||||
public void SetTarget(Transform newTarget)
|
||||
{
|
||||
target = newTarget;
|
||||
if (enableDebug && target != null)
|
||||
Debug.Log($"[CrystalShooterAI] Set target: {target.name}");
|
||||
}
|
||||
/// <summary> Sets the turret's aiming target (does NOT propagate to projectiles). </summary>
|
||||
public void SetTarget(Transform newTarget) => target = newTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Starts the timed shooting routine (fires until maxShots, then despawns).
|
||||
/// </summary>
|
||||
/// <summary> Starts the timed shooting routine (fires until maxShots, then despawns). </summary>
|
||||
public void StartShooting()
|
||||
{
|
||||
if (isActive) return;
|
||||
isActive = true;
|
||||
shotsFired = 0;
|
||||
|
||||
shootingCoroutine = StartCoroutine(ShootingCoroutine());
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Starting shooting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the shooting routine immediately.
|
||||
/// </summary>
|
||||
/// <summary> Stops the shooting routine immediately. </summary>
|
||||
public void StopShooting()
|
||||
{
|
||||
isActive = false;
|
||||
|
||||
if (shootingCoroutine != null)
|
||||
{
|
||||
StopCoroutine(shootingCoroutine);
|
||||
shootingCoroutine = null;
|
||||
}
|
||||
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Stopped shooting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main shooting loop: checks aim/range → spawns fireball → waits fireRate.
|
||||
/// After finishing, waits a short delay and despawns the turret.
|
||||
/// Main shooting loop with initial spawn stagger and per-shot jitter.
|
||||
/// </summary>
|
||||
private IEnumerator ShootingCoroutine()
|
||||
{
|
||||
// 1) Initial stagger so multiple crystals don't start at the same frame
|
||||
if (initialStaggerRange.y > 0f)
|
||||
{
|
||||
float stagger = Random.Range(initialStaggerRange.x, initialStaggerRange.y);
|
||||
if (stagger > 0f) yield return new WaitForSeconds(stagger);
|
||||
}
|
||||
|
||||
// 2) Normal loop with CanShoot gate and per-shot jittered waits
|
||||
while (shotsFired < maxShots && isActive)
|
||||
{
|
||||
if (CanShoot())
|
||||
@@ -186,18 +163,17 @@ namespace DemonBoss.Magic
|
||||
lastShotTime = Time.time;
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(fireRate);
|
||||
float wait = fireRate + (fireRateJitter > 0f ? Random.Range(-fireRateJitter, fireRateJitter) : 0f);
|
||||
// Clamp wait to something safe so it never becomes non-positive
|
||||
if (wait < 0.05f) wait = 0.05f;
|
||||
yield return new WaitForSeconds(wait);
|
||||
}
|
||||
|
||||
if (enableDebug) Debug.Log($"[CrystalShooterAI] Finished shooting ({shotsFired} shots)");
|
||||
|
||||
yield return new WaitForSeconds(despawnDelay);
|
||||
DespawnCrystal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aiming/range gate for firing.
|
||||
/// </summary>
|
||||
/// <summary> Aiming/range gate for firing. </summary>
|
||||
private bool CanShoot()
|
||||
{
|
||||
if (target == null) return false;
|
||||
@@ -211,16 +187,10 @@ namespace DemonBoss.Magic
|
||||
return angleToTarget <= aimTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns a fireball oriented towards the turret's current aim direction.
|
||||
/// </summary>
|
||||
/// <summary> Spawns a fireball oriented towards the turret's current aim direction with optional dispersion. </summary>
|
||||
private void FireFireball()
|
||||
{
|
||||
if (fireballPrefab == null || muzzle == null)
|
||||
{
|
||||
if (enableDebug) Debug.LogWarning("[CrystalShooterAI] Missing fireball prefab or muzzle");
|
||||
return;
|
||||
}
|
||||
if (fireballPrefab == null || muzzle == null) return;
|
||||
|
||||
Vector3 shootDirection;
|
||||
if (target != null)
|
||||
@@ -228,28 +198,24 @@ namespace DemonBoss.Magic
|
||||
Vector3 targetCenter = target.position + Vector3.up * 1f;
|
||||
shootDirection = (targetCenter - muzzle.position).normalized;
|
||||
}
|
||||
else
|
||||
else shootDirection = crystalTransform.forward;
|
||||
|
||||
// Apply small aim jitter (random yaw/pitch) to avoid perfect sync volleys
|
||||
if (aimJitterDegrees > 0f)
|
||||
{
|
||||
shootDirection = crystalTransform.forward;
|
||||
float yaw = Random.Range(-aimJitterDegrees, aimJitterDegrees);
|
||||
float pitch = Random.Range(-aimJitterDegrees * 0.5f, aimJitterDegrees * 0.5f); // usually less pitch dispersion
|
||||
shootDirection = Quaternion.Euler(pitch, yaw, 0f) * shootDirection;
|
||||
}
|
||||
|
||||
Vector3 spawnPosition = muzzle.position;
|
||||
Quaternion spawnRotation = Quaternion.LookRotation(shootDirection);
|
||||
|
||||
LeanPool.Spawn(fireballPrefab, spawnPosition, spawnRotation);
|
||||
|
||||
PlayShootEffects();
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
Debug.Log($"[CrystalShooterAI] Shot #{shotsFired + 1} at {spawnPosition} dir: {shootDirection}");
|
||||
Debug.DrawRay(spawnPosition, shootDirection * 8f, Color.red, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays muzzle VFX and shoot SFX (if enabled).
|
||||
/// </summary>
|
||||
/// <summary> Plays muzzle VFX and shoot SFX (if enabled). </summary>
|
||||
private void PlayShootEffects()
|
||||
{
|
||||
if (!useShootEffects) return;
|
||||
@@ -261,14 +227,10 @@ namespace DemonBoss.Magic
|
||||
}
|
||||
|
||||
if (audioSource != null && shootSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(shootSound);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooth yaw rotation towards target; idles by spinning when no target.
|
||||
/// </summary>
|
||||
/// <summary> Smooth yaw rotation towards target; idles by spinning when no target. </summary>
|
||||
private void RotateTowardsTarget()
|
||||
{
|
||||
if (target != null)
|
||||
@@ -292,24 +254,15 @@ namespace DemonBoss.Magic
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Despawns the turret via Lean Pool.
|
||||
/// </summary>
|
||||
/// <summary> Despawns the turret via Lean Pool. </summary>
|
||||
public void DespawnCrystal()
|
||||
{
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Despawning crystal");
|
||||
StopShooting();
|
||||
LeanPool.Despawn(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces immediate despawn (e.g., boss death).
|
||||
/// </summary>
|
||||
public void ForceDespawn()
|
||||
{
|
||||
if (enableDebug) Debug.Log("[CrystalShooterAI] Forced despawn");
|
||||
DespawnCrystal();
|
||||
}
|
||||
/// <summary> Forces immediate despawn (e.g., boss death). </summary>
|
||||
public void ForceDespawn() => DespawnCrystal();
|
||||
|
||||
/// <summary> Returns crystal state information. </summary>
|
||||
public bool IsActive() => isActive;
|
||||
@@ -320,9 +273,6 @@ namespace DemonBoss.Magic
|
||||
|
||||
public float GetTimeSinceLastShot() => Time.time - lastShotTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gizmos for range and aim visualization.
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!showGizmos) return;
|
||||
@@ -330,24 +280,6 @@ namespace DemonBoss.Magic
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, maxShootingRange);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawLine(transform.position, target.position);
|
||||
|
||||
if (muzzle != null)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Vector3 forward = transform.forward * 5f;
|
||||
Vector3 right = Quaternion.AngleAxis(aimTolerance, transform.up) * forward;
|
||||
Vector3 left = Quaternion.AngleAxis(-aimTolerance, transform.up) * forward;
|
||||
|
||||
Gizmos.DrawLine(muzzle.position, muzzle.position + right);
|
||||
Gizmos.DrawLine(muzzle.position, muzzle.position + left);
|
||||
Gizmos.DrawLine(muzzle.position, muzzle.position + forward);
|
||||
}
|
||||
}
|
||||
|
||||
if (muzzle != null)
|
||||
{
|
||||
Gizmos.color = Color.blue;
|
||||
|
||||
196
Assets/AI/Demon/Crystals_red 1.mat
Normal file
196
Assets/AI/Demon/Crystals_red 1.mat
Normal file
@@ -0,0 +1,196 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-7966782797081402813
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
version: 10
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Crystals_red 1
|
||||
m_Shader: {fileID: -6465566751694194690, guid: 00a0b3897399f8b42a61a0333dd40ced,
|
||||
type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- BASETEXTYPE_ALBEDO_EMISSIVE
|
||||
- USEDISSOLVE_DONT_USE
|
||||
- USEFRESNEL
|
||||
- _ALPHATEST_ON
|
||||
- _USEDISTANCEFADE
|
||||
m_InvalidKeywords:
|
||||
- _EMISSION
|
||||
- _ENVIRONMENTREFLECTIONS_OFF
|
||||
- _METALLICSPECGLOSSMAP
|
||||
- _NORMALMAP
|
||||
m_LightmapFlags: 2
|
||||
m_EnableInstancingVariants: 1
|
||||
m_DoubleSidedGI: 1
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- BaseTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7905193ef00a1844e9b9e2c14ce9be7f, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- DissolveMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- NOSMap:
|
||||
m_Texture: {fileID: 2800000, guid: ead716f17a0a0f347adaf3ff0a109e6a, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: c0d899801acbe0845902935b9e0e857e, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 5a004b07e91ff744aa9143b4cdd46a2d, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 2800000, guid: 284695d770a91574cb92ebad906dc6f4, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c0d899801acbe0845902935b9e0e857e, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 2800000, guid: 18c6ef854a1f94045bcd407172b8d0f6, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 2800000, guid: 18c6ef854a1f94045bcd407172b8d0f6, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- AOStrength: 3
|
||||
- AlphaClipThreshold: 0.454
|
||||
- BASETEXTYPE: 0
|
||||
- DissolveNoiseScale: 25
|
||||
- EffectStrenght: 1
|
||||
- FresnelPower: 1.37
|
||||
- Metalness: 0.13
|
||||
- NormalStrength: 1.64
|
||||
- Smoothness: 0
|
||||
- USEDISSOLVE: 0
|
||||
- USEDISSOLVEMASK: 0
|
||||
- USEFRESNEL: 1
|
||||
- Vector1_473704f964214ae2bc68475022d1524b: 0.05
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _BendEffect: 0
|
||||
- _BendMaxDistance: 1
|
||||
- _BendMaxHeight: 0
|
||||
- _BendMinDistance: 0.2
|
||||
- _BendMinHeight: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 0
|
||||
- _BumpScale: 1
|
||||
- _CastShadows: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _EffectThreshold: 0
|
||||
- _EmissionScaleUI: 1.5
|
||||
- _EnvironmentReflections: 0
|
||||
- _FadeDistance: 0.28
|
||||
- _FarFadeDistance: 500
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0.613
|
||||
- _GlossyReflections: 0
|
||||
- _InverseFadeRange: 1
|
||||
- _InverseFarFadeRange: 0.5
|
||||
- _Metallic: 0.772
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueControl: 0
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Surface: 0
|
||||
- _Threshold: 0.184
|
||||
- _USEDISTANCEFADE: 1
|
||||
- _USESCANWAVE: 0
|
||||
- _UVSec: 0
|
||||
- _WaveTrail: 4
|
||||
- _WorkflowMode: 1
|
||||
- _ZTest: 4
|
||||
- _ZWrite: 1
|
||||
- _ZWriteControl: 0
|
||||
m_Colors:
|
||||
- BaseColor: {r: 0.92593974, g: 0.92593974, b: 0.92593974, a: 1}
|
||||
- Color_613d1588816440ec9b17710effb7528b: {r: 0, g: 13.98681, b: 714.8679, a: 0}
|
||||
- EmissiveColor: {r: 0.6886792, g: 0.16098994, b: 0, a: 1.5}
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _BendVector: {r: 0, g: -1, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 29.508846, g: 3.0899315, b: 0, a: 1.5}
|
||||
- _EmissionColorUI: {r: 1, g: 0.10344824, b: 0, a: 1}
|
||||
- _FresnelColor: {r: 1, g: 0.113483936, b: 0, a: 0}
|
||||
- _ScanWaveColor: {r: 0, g: 0.5949242, b: 1, a: 0}
|
||||
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
8
Assets/AI/Demon/Crystals_red 1.mat.meta
Normal file
8
Assets/AI/Demon/Crystals_red 1.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c11c6b446cf6c4b4cb5b5cd72263e342
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
196
Assets/AI/Demon/Crystals_red 2.mat
Normal file
196
Assets/AI/Demon/Crystals_red 2.mat
Normal file
@@ -0,0 +1,196 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-7966782797081402813
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
version: 10
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Crystals_red 2
|
||||
m_Shader: {fileID: -6465566751694194690, guid: 00a0b3897399f8b42a61a0333dd40ced,
|
||||
type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- BASETEXTYPE_ALBEDO_EMISSIVE
|
||||
- USEDISSOLVE_DONT_USE
|
||||
- USEFRESNEL
|
||||
- _ALPHATEST_ON
|
||||
- _USEDISTANCEFADE
|
||||
m_InvalidKeywords:
|
||||
- _EMISSION
|
||||
- _ENVIRONMENTREFLECTIONS_OFF
|
||||
- _METALLICSPECGLOSSMAP
|
||||
- _NORMALMAP
|
||||
m_LightmapFlags: 2
|
||||
m_EnableInstancingVariants: 1
|
||||
m_DoubleSidedGI: 1
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- BaseTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7905193ef00a1844e9b9e2c14ce9be7f, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- DissolveMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- NOSMap:
|
||||
m_Texture: {fileID: 2800000, guid: ead716f17a0a0f347adaf3ff0a109e6a, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: c0d899801acbe0845902935b9e0e857e, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 5a004b07e91ff744aa9143b4cdd46a2d, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 2800000, guid: 284695d770a91574cb92ebad906dc6f4, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c0d899801acbe0845902935b9e0e857e, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 2800000, guid: 18c6ef854a1f94045bcd407172b8d0f6, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 2800000, guid: 18c6ef854a1f94045bcd407172b8d0f6, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- AOStrength: 3
|
||||
- AlphaClipThreshold: 0.454
|
||||
- BASETEXTYPE: 0
|
||||
- DissolveNoiseScale: 25
|
||||
- EffectStrenght: 1
|
||||
- FresnelPower: 1.37
|
||||
- Metalness: 0.13
|
||||
- NormalStrength: 1.64
|
||||
- Smoothness: 0
|
||||
- USEDISSOLVE: 0
|
||||
- USEDISSOLVEMASK: 0
|
||||
- USEFRESNEL: 1
|
||||
- Vector1_473704f964214ae2bc68475022d1524b: 0.05
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _BendEffect: 0
|
||||
- _BendMaxDistance: 1
|
||||
- _BendMaxHeight: 0
|
||||
- _BendMinDistance: 0.2
|
||||
- _BendMinHeight: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 0
|
||||
- _BumpScale: 1
|
||||
- _CastShadows: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _EffectThreshold: 0
|
||||
- _EmissionScaleUI: 1.5
|
||||
- _EnvironmentReflections: 0
|
||||
- _FadeDistance: 0.28
|
||||
- _FarFadeDistance: 500
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0.613
|
||||
- _GlossyReflections: 0
|
||||
- _InverseFadeRange: 1
|
||||
- _InverseFarFadeRange: 0.5
|
||||
- _Metallic: 0.772
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueControl: 0
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _Surface: 0
|
||||
- _Threshold: 0.184
|
||||
- _USEDISTANCEFADE: 1
|
||||
- _USESCANWAVE: 0
|
||||
- _UVSec: 0
|
||||
- _WaveTrail: 4
|
||||
- _WorkflowMode: 1
|
||||
- _ZTest: 4
|
||||
- _ZWrite: 1
|
||||
- _ZWriteControl: 0
|
||||
m_Colors:
|
||||
- BaseColor: {r: 0.92593974, g: 0.92593974, b: 0.92593974, a: 1}
|
||||
- Color_613d1588816440ec9b17710effb7528b: {r: 0, g: 13.98681, b: 714.8679, a: 0}
|
||||
- EmissiveColor: {r: 1, g: 1, b: 1, a: 1.5}
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _BendVector: {r: 0, g: -1, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 29.508846, g: 3.0899315, b: 0, a: 1.5}
|
||||
- _EmissionColorUI: {r: 1, g: 0.10344824, b: 0, a: 1}
|
||||
- _FresnelColor: {r: 1, g: 1, b: 1, a: 0}
|
||||
- _ScanWaveColor: {r: 0, g: 0.5949242, b: 1, a: 0}
|
||||
- _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
8
Assets/AI/Demon/Crystals_red 2.mat.meta
Normal file
8
Assets/AI/Demon/Crystals_red 2.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f94eea72c30ac2e45ab55894421ea48c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,7 +12,7 @@ GameObject:
|
||||
- component: {fileID: 8933020562711606400}
|
||||
- component: {fileID: 2735702040698985183}
|
||||
- component: {fileID: 4889297222623638116}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: Shield
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4828,7 +4828,7 @@ GameObject:
|
||||
- component: {fileID: 1705028462786019458}
|
||||
- component: {fileID: 636964402995770057}
|
||||
- component: {fileID: 3712794430867809983}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: TriggerEnter
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4900,7 +4900,7 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 5385552833772178802}
|
||||
- component: {fileID: 6288768711713787120}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: PerPlatformSettings
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4948,7 +4948,10 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7058331355936229169}
|
||||
- component: {fileID: 1024337125332675931}
|
||||
m_Layer: 0
|
||||
- component: {fileID: -842443613141667859}
|
||||
- component: {fileID: 4897060740913123605}
|
||||
- component: {fileID: 6803317444044382316}
|
||||
m_Layer: 30
|
||||
m_Name: DemonShield
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4998,7 +5001,87 @@ CapsuleCollider:
|
||||
m_Radius: 2.5
|
||||
m_Height: 2.5
|
||||
m_Direction: 1
|
||||
m_Center: {x: 0, y: 1.5, z: 0}
|
||||
m_Center: {x: 0, y: 1.5, z: -0.8}
|
||||
--- !u!114 &-842443613141667859
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4089514887418515818}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 64156f4d0e036104895ef313e63c6b09, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
growDuration: 0.5
|
||||
scaleCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
--- !u!136 &4897060740913123605
|
||||
CapsuleCollider:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4089514887418515818}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 1
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_Radius: 3
|
||||
m_Height: 3
|
||||
m_Direction: 1
|
||||
m_Center: {x: 0, y: 1.5, z: -0.8}
|
||||
--- !u!114 &6803317444044382316
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4089514887418515818}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3a4b288b220c78b4ca00e13cb8a72d6b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
damage: 15
|
||||
knockbackForce: 10
|
||||
playerTag: Player
|
||||
damageInterval: 1
|
||||
damageSound: {fileID: 8300000, guid: 0af332725d9792840a4caa29e8991d08, type: 3}
|
||||
damageEffect: {fileID: 1606542427775616, guid: 0de6da49dab6f8b4d93ab32a4cb441af,
|
||||
type: 3}
|
||||
enableDebug: 0
|
||||
--- !u!1 &5185508652979790054
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -5011,7 +5094,7 @@ GameObject:
|
||||
- component: {fileID: 2972078956396372929}
|
||||
- component: {fileID: 2302274119979875947}
|
||||
- component: {fileID: 7352519013254002557}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: ShieldAdd
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -9833,7 +9916,7 @@ GameObject:
|
||||
- component: {fileID: 4294035958862554727}
|
||||
- component: {fileID: 8165694420557042206}
|
||||
- component: {fileID: 5904661144955708609}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: ShieldFringe
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -14651,10 +14734,34 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 7058331355936229169}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1137363236680030, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1300024870466346, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1530244028065882, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1606657272102914, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1849043180036032, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: shield3
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4855141465030352, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -0.76840776
|
||||
@@ -14748,14 +14855,38 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 7058331355936229169}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1137363236680030, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1300024870466346, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1530244028065882, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1606657272102914, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Shield02
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1606657272102914, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1849043180036032, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: sield2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4855141465030352, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0.561755
|
||||
@@ -14849,10 +14980,34 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 7058331355936229169}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1137363236680030, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1300024870466346, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1530244028065882, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1606657272102914, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1849043180036032, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: shield3
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1881634734537756, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4855141465030352, guid: 5b389d585d7681948a86765d14232bdb, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -0.123969555
|
||||
|
||||
367
Assets/AI/Demon/DestroyableTurret.cs
Normal file
367
Assets/AI/Demon/DestroyableTurret.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using Invector;
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DemonBoss.Magic
|
||||
{
|
||||
/// <summary>
|
||||
/// Turret component that allows player to destroy crystal turrets
|
||||
/// Implements Invector interfaces for damage system integration
|
||||
/// Attach to turret prefab along with appropriate collider (non-trigger)
|
||||
/// </summary>
|
||||
public class DestroyableTurret : MonoBehaviour, vIDamageReceiver, vIHealthController
|
||||
{
|
||||
[Header("Health Configuration")]
|
||||
[Tooltip("Maximum health points of the turret")]
|
||||
public int maxHealth = 50;
|
||||
|
||||
[Tooltip("Current health points (read-only)")]
|
||||
[SerializeField] private int currentHealth;
|
||||
|
||||
[Header("Destruction Effects")]
|
||||
[Tooltip("Visual effect played when turret is destroyed")]
|
||||
public GameObject destructionEffect;
|
||||
|
||||
[Tooltip("Sound played when turret is destroyed")]
|
||||
public AudioClip destructionSound;
|
||||
|
||||
[Tooltip("Sound played when turret receives damage")]
|
||||
public AudioClip hitSound;
|
||||
|
||||
[Header("Visual Feedback")]
|
||||
[Tooltip("Material applied when turret is damaged")]
|
||||
public Material damagedMaterial;
|
||||
|
||||
[Tooltip("HP threshold below which damaged material is applied (percentage)")]
|
||||
[Range(0f, 1f)]
|
||||
public float damagedThreshold = 0.5f;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
// Private fields
|
||||
private AudioSource audioSource;
|
||||
|
||||
private CrystalShooterAI shooterAI;
|
||||
private Renderer turretRenderer;
|
||||
private Material originalMaterial;
|
||||
private bool isDestroyed = false;
|
||||
|
||||
// Invector system events
|
||||
public UnityEngine.Events.UnityEvent OnReceiveDamage { get; set; }
|
||||
|
||||
public UnityEngine.Events.UnityEvent OnDead { get; set; }
|
||||
|
||||
// vIHealthController implementation
|
||||
public int currentHealthRecovery { get; set; }
|
||||
|
||||
public int maxHealthRecovery { get; set; }
|
||||
public bool isDead => currentHealth <= 0;
|
||||
|
||||
public OnReceiveDamage onStartReceiveDamage => throw new System.NotImplementedException();
|
||||
|
||||
public OnReceiveDamage onReceiveDamage => throw new System.NotImplementedException();
|
||||
|
||||
public OnDead onDead => throw new System.NotImplementedException();
|
||||
|
||||
float vIHealthController.currentHealth => throw new System.NotImplementedException();
|
||||
|
||||
public int MaxHealth => throw new System.NotImplementedException();
|
||||
|
||||
bool vIHealthController.isDead { get => isDead; set => throw new System.NotImplementedException(); }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize components and validate setup
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
InitializeComponents();
|
||||
InitializeHealth();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find and configure required components
|
||||
/// </summary>
|
||||
private void InitializeComponents()
|
||||
{
|
||||
// Find components
|
||||
shooterAI = GetComponent<CrystalShooterAI>();
|
||||
turretRenderer = GetComponent<Renderer>();
|
||||
|
||||
// Store original material
|
||||
if (turretRenderer != null)
|
||||
{
|
||||
originalMaterial = turretRenderer.material;
|
||||
}
|
||||
|
||||
// Setup AudioSource
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
if (audioSource == null && (destructionSound != null || hitSound != null))
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.spatialBlend = 1f; // 3D sound
|
||||
}
|
||||
|
||||
// Validate collider setup
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null && col.isTrigger && enableDebug)
|
||||
{
|
||||
Debug.LogWarning("[DestroyableTurret] Collider should not be trigger for damage system!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup initial health values
|
||||
/// </summary>
|
||||
private void InitializeHealth()
|
||||
{
|
||||
currentHealth = maxHealth;
|
||||
maxHealthRecovery = maxHealth;
|
||||
currentHealthRecovery = 0;
|
||||
|
||||
if (enableDebug) Debug.Log($"[DestroyableTurret] Initialized with {currentHealth}/{maxHealth} HP");
|
||||
}
|
||||
|
||||
#region vIDamageReceiver Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Handle incoming damage from Invector damage system
|
||||
/// </summary>
|
||||
public void TakeDamage(vDamage damage)
|
||||
{
|
||||
if (isDestroyed) return;
|
||||
|
||||
int damageValue = Mathf.RoundToInt(damage.damageValue);
|
||||
TakeDamage(damageValue);
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[DestroyableTurret] Received {damageValue} damage from {damage.sender?.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle incoming damage with hit reaction parameter
|
||||
/// </summary>
|
||||
public void TakeDamage(vDamage damage, bool hitReaction)
|
||||
{
|
||||
TakeDamage(damage);
|
||||
}
|
||||
|
||||
#endregion vIDamageReceiver Implementation
|
||||
|
||||
#region vIHealthController Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Apply damage to the turret and handle destruction
|
||||
/// </summary>
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
if (isDestroyed || currentHealth <= 0) return;
|
||||
|
||||
currentHealth = Mathf.Max(0, currentHealth - damage);
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[DestroyableTurret] HP: {currentHealth}/{maxHealth} (-{damage})");
|
||||
|
||||
// Play hit effects
|
||||
PlayHitEffects();
|
||||
UpdateVisualState();
|
||||
|
||||
// Trigger damage event
|
||||
OnReceiveDamage?.Invoke();
|
||||
|
||||
// Check if destroyed
|
||||
if (currentHealth <= 0)
|
||||
{
|
||||
DestroyTurret();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change health by specified amount (positive for healing, negative for damage)
|
||||
/// </summary>
|
||||
public void ChangeHealth(int value)
|
||||
{
|
||||
if (isDestroyed) return;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
TakeDamage(-value);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentHealth = Mathf.Min(maxHealth, currentHealth + value);
|
||||
if (enableDebug) Debug.Log($"[DestroyableTurret] Healed by {value}, HP: {currentHealth}/{maxHealth}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify maximum health value
|
||||
/// </summary>
|
||||
public void ChangeMaxHealth(int value)
|
||||
{
|
||||
maxHealth = Mathf.Max(1, maxHealth + value);
|
||||
currentHealth = Mathf.Min(currentHealth, maxHealth);
|
||||
}
|
||||
|
||||
#endregion vIHealthController Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Play sound and visual effects when receiving damage
|
||||
/// </summary>
|
||||
private void PlayHitEffects()
|
||||
{
|
||||
// Play hit sound
|
||||
if (audioSource != null && hitSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(hitSound);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visual appearance based on current health
|
||||
/// </summary>
|
||||
private void UpdateVisualState()
|
||||
{
|
||||
// Change material if turret is heavily damaged
|
||||
if (turretRenderer != null && damagedMaterial != null)
|
||||
{
|
||||
float healthPercent = (float)currentHealth / maxHealth;
|
||||
if (healthPercent <= damagedThreshold && turretRenderer.material != damagedMaterial)
|
||||
{
|
||||
turretRenderer.material = damagedMaterial;
|
||||
if (enableDebug) Debug.Log("[DestroyableTurret] Changed to damaged material");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle turret destruction sequence
|
||||
/// </summary>
|
||||
private void DestroyTurret()
|
||||
{
|
||||
if (isDestroyed) return;
|
||||
isDestroyed = true;
|
||||
|
||||
if (enableDebug) Debug.Log("[DestroyableTurret] Turret has been destroyed!");
|
||||
|
||||
// Stop shooting
|
||||
if (shooterAI != null)
|
||||
{
|
||||
shooterAI.StopShooting();
|
||||
}
|
||||
|
||||
// Trigger death event
|
||||
OnDead?.Invoke();
|
||||
|
||||
// Play destruction effects
|
||||
PlayDestructionEffects();
|
||||
|
||||
// Destroy after brief delay (let effects play)
|
||||
StartCoroutine(DestroyAfterDelay(0.1f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play visual and audio effects for turret destruction
|
||||
/// </summary>
|
||||
private void PlayDestructionEffects()
|
||||
{
|
||||
// Spawn visual effect
|
||||
if (destructionEffect != null)
|
||||
{
|
||||
GameObject effect = Instantiate(destructionEffect, transform.position, transform.rotation);
|
||||
Destroy(effect, 5f);
|
||||
}
|
||||
|
||||
// Play destruction sound
|
||||
if (audioSource != null && destructionSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(destructionSound);
|
||||
|
||||
// Keep AudioSource alive to finish playing
|
||||
audioSource.transform.parent = null;
|
||||
Destroy(audioSource.gameObject, destructionSound.length + 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine to destroy turret after specified delay
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator DestroyAfterDelay(float delay)
|
||||
{
|
||||
yield return new UnityEngine.WaitForSeconds(delay);
|
||||
|
||||
// Remove through Lean Pool (if used) or regular Destroy
|
||||
if (gameObject.scene.isLoaded) // Check if object still exists
|
||||
{
|
||||
LeanPool.Despawn(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force immediate turret destruction (e.g., when boss dies)
|
||||
/// </summary>
|
||||
public void ForceDestroy()
|
||||
{
|
||||
if (enableDebug) Debug.Log("[DestroyableTurret] Forced destruction");
|
||||
currentHealth = 0;
|
||||
//isDead = true;
|
||||
DestroyTurret();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns current health as percentage of maximum health
|
||||
/// </summary>
|
||||
public float GetHealthPercentage()
|
||||
{
|
||||
return maxHealth > 0 ? (float)currentHealth / maxHealth : 0f;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Draw turret status visualization in Scene View
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Show turret status
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Gizmos.color = currentHealth > maxHealth * 0.5f ? Color.green :
|
||||
currentHealth > maxHealth * 0.25f ? Color.yellow : Color.red;
|
||||
}
|
||||
else
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
}
|
||||
|
||||
Gizmos.DrawWireCube(transform.position + Vector3.up * 2f, Vector3.one * 0.5f);
|
||||
|
||||
// HP text in Scene View
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
UnityEditor.Handles.Label(transform.position + Vector3.up * 2.5f, $"HP: {currentHealth}/{maxHealth}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void AddHealth(int value)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void ResetHealth(float health)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public void ResetHealth()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2
Assets/AI/Demon/DestroyableTurret.cs.meta
Normal file
2
Assets/AI/Demon/DestroyableTurret.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06579ea47ceeddd42a05f7720c15b5de
|
||||
@@ -11,7 +11,7 @@ GameObject:
|
||||
- component: {fileID: 4577187839491108}
|
||||
- component: {fileID: 198086061384069858}
|
||||
- component: {fileID: 199127982807948490}
|
||||
m_Layer: 2
|
||||
m_Layer: 30
|
||||
m_Name: FireEmbers (4)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4768,7 +4768,7 @@ GameObject:
|
||||
- component: {fileID: 199577645087675124}
|
||||
- component: {fileID: -5586632368230897359}
|
||||
- component: {fileID: 6785567375430979834}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: FireBall
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -6246,8 +6246,8 @@ ParticleSystem:
|
||||
rowMode: 1
|
||||
sprites:
|
||||
- sprite: {fileID: 0}
|
||||
flipU: 0.5
|
||||
flipV: 0.5
|
||||
flipU: 0
|
||||
flipV: 0
|
||||
VelocityModule:
|
||||
enabled: 0
|
||||
x:
|
||||
@@ -9584,7 +9584,7 @@ ParticleSystemRenderer:
|
||||
m_ShadowBias: 0
|
||||
m_RenderAlignment: 0
|
||||
m_Pivot: {x: 0, y: 0, z: 0}
|
||||
m_Flip: {x: 0, y: 0, z: 0}
|
||||
m_Flip: {x: 0.5, y: 0.5, z: 0}
|
||||
m_EnableGPUInstancing: 1
|
||||
m_ApplyActiveColorSpace: 1
|
||||
m_AllowRoll: 1
|
||||
@@ -9622,10 +9622,10 @@ CapsuleCollider:
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_Radius: 0.5
|
||||
m_Height: 0.1
|
||||
m_Radius: 1
|
||||
m_Height: 0
|
||||
m_Direction: 1
|
||||
m_Center: {x: 0.31801516, y: 0, z: 0}
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &6785567375430979834
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -9639,10 +9639,11 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
targetTag: Player
|
||||
targetHeightOffset: 1
|
||||
speed: 6
|
||||
lockTime: 15
|
||||
maxLifeTime: 30
|
||||
lockTime: 0.5
|
||||
maxLifeTime: 60
|
||||
arrivalTolerance: 0.25
|
||||
damage: 25
|
||||
damage: 5
|
||||
knockbackForce: 5
|
||||
enableDebug: 0
|
||||
|
||||
@@ -11,7 +11,7 @@ GameObject:
|
||||
- component: {fileID: 4577187839491108}
|
||||
- component: {fileID: 198086061384069858}
|
||||
- component: {fileID: 199127982807948490}
|
||||
m_Layer: 2
|
||||
m_Layer: 30
|
||||
m_Name: FireEmbers (4)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -4769,7 +4769,7 @@ GameObject:
|
||||
- component: {fileID: 199577645087675124}
|
||||
- component: {fileID: -5586632368230897359}
|
||||
- component: {fileID: -745564043032181229}
|
||||
m_Layer: 0
|
||||
m_Layer: 30
|
||||
m_Name: Meteor
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -9639,30 +9639,35 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 0d6ec41ae03923f45a963ca66dbb6c56, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
targetTag: Player
|
||||
useOverrideImpactPoint: 0
|
||||
overrideImpactPoint: {x: 0, y: 0, z: 0}
|
||||
snapImpactToGround: 1
|
||||
groundMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1410334711
|
||||
spawnHeightAboveTarget: 12
|
||||
entryAngleDownFromHorizontal: 20
|
||||
azimuthAimWeight: 0.85
|
||||
groundSnapRayStart: 3
|
||||
groundSnapRayLength: 30
|
||||
impactPoint: {x: 0, y: 0, z: 0}
|
||||
speed: 30
|
||||
gravityLikeAcceleration: 1
|
||||
rotateToVelocity: 1
|
||||
maxLifeTime: 12
|
||||
gravityLikePull: 14
|
||||
spawnHeightAboveTarget: 12
|
||||
arriveEpsilon: 0.35
|
||||
maxLifetime: 30
|
||||
traceRadius: 0.45
|
||||
stopOnLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1410334711
|
||||
damageLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1410334711
|
||||
explosionRadius: 5
|
||||
explosionMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1410334711
|
||||
aoeOnlyTargetTag: 0
|
||||
castRadius: 0.25
|
||||
collisionMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1410334711
|
||||
damage: 40
|
||||
knockbackForce: 5
|
||||
enableDebug: 0
|
||||
impactVfxPrefab: {fileID: 1606542427775616, guid: 0de6da49dab6f8b4d93ab32a4cb441af,
|
||||
type: 3}
|
||||
onSpawn:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
onImpact:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -9678,6 +9683,26 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: Meteor
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 100000, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 100048, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 100050, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 100052, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 100054, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_Layer
|
||||
value: 30
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 400000, guid: 638572418cf33664f88dae09c5bf7652, type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 20
|
||||
|
||||
@@ -6,278 +6,287 @@ using UnityEngine.Events;
|
||||
|
||||
namespace DemonBoss.Magic
|
||||
{
|
||||
/// <summary>
|
||||
/// Enhanced meteor projectile with fireball-like tracking mechanics
|
||||
/// Can either track player or fly to a locked impact point
|
||||
/// </summary>
|
||||
public class MeteorProjectile : MonoBehaviour
|
||||
{
|
||||
#region Configuration
|
||||
#region Inspector: Targeting
|
||||
|
||||
[Header("Targeting")]
|
||||
[Tooltip("Tag of the target to pick impact point from on spawn")]
|
||||
[Tooltip("If true, use 'overrideImpactPoint' instead of tracking player")]
|
||||
public bool useOverrideImpactPoint = false;
|
||||
|
||||
[Tooltip("Externally provided locked impact point")]
|
||||
public Vector3 overrideImpactPoint;
|
||||
|
||||
[Tooltip("Tag to find and track if not using override point")]
|
||||
public string targetTag = "Player";
|
||||
|
||||
[Tooltip("If true, raycasts down from impact point to find ground (better visuals)")]
|
||||
[Tooltip("Height offset for targeting (aim at chest/head)")]
|
||||
public float targetHeightOffset = 1.0f;
|
||||
|
||||
[Tooltip("If true, raycast to ground from impact point")]
|
||||
public bool snapImpactToGround = true;
|
||||
|
||||
[Tooltip("LayerMask used when snapping impact point to ground")]
|
||||
[Tooltip("Layers considered 'ground'")]
|
||||
public LayerMask groundMask = ~0;
|
||||
|
||||
[Header("Entry Geometry")]
|
||||
[Tooltip("If > 0, teleports start Y above impact point by this height (keeps current XZ). 0 = keep original height.")]
|
||||
public float spawnHeightAboveTarget = 12f;
|
||||
[ReadOnlyInInspector] public Vector3 currentTargetPoint;
|
||||
|
||||
[Tooltip("Entry angle measured DOWN from horizontal (e.g. 15° = shallow, 45° = steeper)")]
|
||||
[Range(0f, 89f)]
|
||||
public float entryAngleDownFromHorizontal = 20f;
|
||||
#endregion Inspector: Targeting
|
||||
|
||||
[Tooltip("How closely to aim towards the target in XZ before tilting down (0..1). 1 = purely towards target, 0 = purely downward.")]
|
||||
[Range(0f, 1f)]
|
||||
public float azimuthAimWeight = 0.85f;
|
||||
#region Inspector: Flight
|
||||
|
||||
[Header("Movement")]
|
||||
[Tooltip("Initial speed magnitude in units per second")]
|
||||
public float speed = 30f;
|
||||
[Header("Flight")]
|
||||
[Tooltip("Movement speed in m/s")]
|
||||
public float speed = 25f;
|
||||
|
||||
[Tooltip("Extra downward acceleration to add a slight curve (0 = disabled)")]
|
||||
public float gravityLikeAcceleration = 0f;
|
||||
[Tooltip("Time to track player before locking to position")]
|
||||
public float trackingTime = 1.5f;
|
||||
|
||||
[Tooltip("Rotate the meteor mesh to face velocity")]
|
||||
public bool rotateToVelocity = true;
|
||||
[Tooltip("Distance threshold to consider arrived")]
|
||||
public float arriveEpsilon = 0.5f;
|
||||
|
||||
[Tooltip("Seconds before the meteor auto-despawns")]
|
||||
public float maxLifeTime = 12f;
|
||||
[Tooltip("Max lifetime in seconds")]
|
||||
public float maxLifetime = 15f;
|
||||
|
||||
[Header("Impact / AOE")]
|
||||
[Tooltip("Explosion radius at impact (0 = no AOE, only single target)")]
|
||||
public float explosionRadius = 0f;
|
||||
#endregion Inspector: Flight
|
||||
|
||||
[Tooltip("Layers that should receive AOE damage")]
|
||||
public LayerMask explosionMask = ~0;
|
||||
#region Inspector: Collision & Damage
|
||||
|
||||
[Tooltip("If true, AOE will only damage colliders with the same tag as targetTag")]
|
||||
public bool aoeOnlyTargetTag = false;
|
||||
[Header("Collision & Damage")]
|
||||
[Tooltip("Collision detection radius")]
|
||||
public float collisionRadius = 0.8f;
|
||||
|
||||
[Tooltip("Sphere radius for continuous collision checks")]
|
||||
public float castRadius = 0.25f;
|
||||
[Tooltip("Layers that stop the meteor")]
|
||||
public LayerMask stopOnLayers = ~0;
|
||||
|
||||
[Tooltip("Layers that block the meteor during flight (ground/walls/etc.)")]
|
||||
public LayerMask collisionMask = ~0;
|
||||
[Tooltip("Layers that take damage")]
|
||||
public LayerMask damageLayers = ~0;
|
||||
|
||||
[Tooltip("Damage that will be dealt to Player")]
|
||||
public float damage = 40.0f;
|
||||
[Tooltip("Explosion damage radius")]
|
||||
public float explosionRadius = 4f;
|
||||
|
||||
[Tooltip("Knockback that will be applied to Player")]
|
||||
public float knockbackForce = 5.0f;
|
||||
[Tooltip("Base damage")]
|
||||
public int damage = 35;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable verbose logs and debug rays")]
|
||||
public bool enableDebug = false;
|
||||
[Tooltip("Knockback force")]
|
||||
public float knockbackForce = 12f;
|
||||
|
||||
[Header("Events")]
|
||||
[Tooltip("Invoked once on any impact (use for VFX/SFX/CameraShake)")]
|
||||
#endregion Inspector: Collision & Damage
|
||||
|
||||
#region Inspector: Effects
|
||||
|
||||
[Header("Effects & Events")]
|
||||
public GameObject impactVfxPrefab;
|
||||
|
||||
public UnityEvent onSpawn;
|
||||
public UnityEvent onImpact;
|
||||
|
||||
#endregion Configuration
|
||||
[Header("Debug")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
#endregion Inspector: Effects
|
||||
|
||||
#region Runtime
|
||||
|
||||
private Vector3 impactPoint;
|
||||
private Vector3 velocityDir;
|
||||
private Vector3 velocity;
|
||||
private float timer = 0f;
|
||||
private bool hasDealtDamage = false;
|
||||
private bool hasImpacted = false;
|
||||
private Vector3 prevPos;
|
||||
private Transform _player;
|
||||
private Vector3 _lockedTarget;
|
||||
private bool _isLocked = false;
|
||||
private bool _hasImpacted = false;
|
||||
private float _lifetime = 0f;
|
||||
private readonly Collider[] _overlapCache = new Collider[32];
|
||||
|
||||
#endregion Runtime
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// Resets runtime state, locks the impact point, computes entry direction and initial velocity.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
timer = 0f;
|
||||
hasDealtDamage = false;
|
||||
hasImpacted = false;
|
||||
ResetState();
|
||||
InitializeTargeting();
|
||||
onSpawn?.Invoke();
|
||||
|
||||
// Acquire player and lock impact point
|
||||
Vector3 targetPos = GetPlayerPosition();
|
||||
impactPoint = snapImpactToGround ? SnapPointToGround(targetPos) : targetPos;
|
||||
|
||||
// Optionally start from above to guarantee top-down feel
|
||||
if (spawnHeightAboveTarget > 0f)
|
||||
{
|
||||
Vector3 p = transform.position;
|
||||
p.y = impactPoint.y + spawnHeightAboveTarget;
|
||||
transform.position = p;
|
||||
if (enableDebug) Debug.Log($"[MeteorProjectile] Spawned at {transform.position}");
|
||||
}
|
||||
|
||||
Vector3 toImpactXZ = new Vector3(impactPoint.x, transform.position.y, impactPoint.z) - transform.position;
|
||||
Vector3 azimuthDir = toImpactXZ.sqrMagnitude > 0.0001f ? toImpactXZ.normalized : transform.forward;
|
||||
|
||||
Vector3 tiltAxis = Vector3.Cross(Vector3.up, azimuthDir);
|
||||
if (tiltAxis.sqrMagnitude < 0.0001f) tiltAxis = Vector3.right;
|
||||
Quaternion downTilt = Quaternion.AngleAxis(-entryAngleDownFromHorizontal, tiltAxis);
|
||||
Vector3 tiltedDir = (downTilt * azimuthDir).normalized;
|
||||
|
||||
velocityDir = Vector3.Slerp(Vector3.down, tiltedDir, Mathf.Clamp01(azimuthAimWeight)).normalized;
|
||||
|
||||
velocity = velocityDir * Mathf.Max(0f, speed);
|
||||
|
||||
if (rotateToVelocity && velocity.sqrMagnitude > 0.0001f)
|
||||
transform.rotation = Quaternion.LookRotation(velocity.normalized);
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
Debug.Log($"[Meteor] Impact={impactPoint}, start={transform.position}, dir={velocityDir}");
|
||||
Debug.DrawLine(transform.position, transform.position + velocityDir * 6f, Color.red, 3f);
|
||||
Debug.DrawLine(transform.position, impactPoint, Color.yellow, 2f);
|
||||
}
|
||||
|
||||
prevPos = transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Integrates motion with optional downward acceleration, performs a SphereCast for continuous collision,
|
||||
/// rotates to face velocity, and handles lifetime expiry.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
if (_hasImpacted) return;
|
||||
|
||||
// Downward "gravity-like" acceleration
|
||||
if (gravityLikeAcceleration > 0f)
|
||||
velocity += Vector3.down * gravityLikeAcceleration * Time.deltaTime;
|
||||
_lifetime += Time.deltaTime;
|
||||
|
||||
Vector3 nextPos = transform.position + velocity * Time.deltaTime;
|
||||
|
||||
// Continuous collision: SphereCast from current position toward next
|
||||
Vector3 castDir = nextPos - transform.position;
|
||||
float castDist = castDir.magnitude;
|
||||
if (castDist > 0.0001f)
|
||||
// Check lifetime limit
|
||||
if (_lifetime >= maxLifetime)
|
||||
{
|
||||
if (Physics.SphereCast(transform.position, castRadius, castDir.normalized,
|
||||
out RaycastHit hit, castDist, collisionMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
transform.position = hit.point;
|
||||
Impact(hit.collider, hit.point, hit.normal);
|
||||
if (enableDebug) Debug.Log("[MeteorProjectile] Lifetime expired");
|
||||
DoImpact(transform.position);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No hit — apply movement
|
||||
transform.position = nextPos;
|
||||
|
||||
// Face velocity if requested
|
||||
if (rotateToVelocity && velocity.sqrMagnitude > 0.0001f)
|
||||
transform.rotation = Quaternion.LookRotation(velocity.normalized);
|
||||
|
||||
prevPos = transform.position;
|
||||
|
||||
// Auto-despawn on lifetime cap
|
||||
if (timer >= maxLifeTime)
|
||||
// Handle tracking to lock transition
|
||||
if (!_isLocked && _lifetime >= trackingTime)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[Meteor] Max lifetime reached → Despawn");
|
||||
Despawn();
|
||||
}
|
||||
LockTarget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger-based contact: funnels into unified Impact().
|
||||
/// </summary>
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// Even if not the intended target, a meteor typically impacts anything it touches
|
||||
Impact(other, transform.position, -SafeNormal(velocity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collision-based contact: funnels into unified Impact().
|
||||
/// </summary>
|
||||
private void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
var cp = collision.contacts.Length > 0 ? collision.contacts[0] : default;
|
||||
Impact(collision.collider, cp.point != default ? cp.point : transform.position, cp.normal != default ? cp.normal : Vector3.up);
|
||||
// Update target position and move
|
||||
UpdateTargetPosition();
|
||||
MoveTowardsTarget();
|
||||
CheckCollisions();
|
||||
}
|
||||
|
||||
#endregion Unity
|
||||
|
||||
#region Helpers: Target / Ground
|
||||
#region Targeting & Movement
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target's current position (by tag). If not found, projects forward from current position.
|
||||
/// </summary>
|
||||
private Vector3 GetPlayerPosition()
|
||||
private void ResetState()
|
||||
{
|
||||
GameObject playerObj = GameObject.FindGameObjectWithTag(targetTag);
|
||||
if (playerObj != null)
|
||||
return playerObj.transform.position;
|
||||
|
||||
if (enableDebug) Debug.LogWarning($"[Meteor] No target with tag '{targetTag}' found, using forward guess.");
|
||||
return transform.position + transform.forward * 10f;
|
||||
_hasImpacted = false;
|
||||
_isLocked = false;
|
||||
_lifetime = 0f;
|
||||
_lockedTarget = Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raycasts down from above the source point to find ground; returns original point if no hit.
|
||||
/// </summary>
|
||||
private Vector3 SnapPointToGround(Vector3 source)
|
||||
private void InitializeTargeting()
|
||||
{
|
||||
Vector3 start = source + Vector3.up * 50f;
|
||||
if (Physics.Raycast(start, Vector3.down, out RaycastHit hit, 200f, groundMask, QueryTriggerInteraction.Ignore))
|
||||
return hit.point;
|
||||
if (useOverrideImpactPoint)
|
||||
{
|
||||
_lockedTarget = snapImpactToGround ? SnapToGround(overrideImpactPoint) : overrideImpactPoint;
|
||||
_isLocked = true;
|
||||
currentTargetPoint = _lockedTarget;
|
||||
|
||||
return source;
|
||||
if (enableDebug) Debug.Log($"[MeteorProjectile] Using override target: {_lockedTarget}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find player for tracking
|
||||
var playerGO = GameObject.FindGameObjectWithTag(targetTag);
|
||||
_player = playerGO ? playerGO.transform : null;
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
if (_player) Debug.Log($"[MeteorProjectile] Found player: {_player.name}");
|
||||
else Debug.LogWarning($"[MeteorProjectile] No player found with tag: {targetTag}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a safe normalized version of a vector (falls back to Vector3.down if tiny).
|
||||
/// </summary>
|
||||
private static Vector3 SafeNormal(Vector3 v)
|
||||
private void LockTarget()
|
||||
{
|
||||
return v.sqrMagnitude > 0.0001f ? v.normalized : Vector3.down;
|
||||
if (_isLocked) return;
|
||||
|
||||
if (_player != null)
|
||||
{
|
||||
Vector3 targetPos = _player.position + Vector3.up * targetHeightOffset;
|
||||
_lockedTarget = snapImpactToGround ? SnapToGround(targetPos) : targetPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: lock to current forward direction
|
||||
_lockedTarget = transform.position + transform.forward * 50f;
|
||||
}
|
||||
|
||||
#endregion Helpers: Target / Ground
|
||||
_isLocked = true;
|
||||
|
||||
if (enableDebug) Debug.Log($"[MeteorProjectile] Target locked to: {_lockedTarget}");
|
||||
}
|
||||
|
||||
private void UpdateTargetPosition()
|
||||
{
|
||||
if (_isLocked)
|
||||
{
|
||||
currentTargetPoint = _lockedTarget;
|
||||
}
|
||||
else if (_player != null)
|
||||
{
|
||||
currentTargetPoint = _player.position + Vector3.up * targetHeightOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No player, keep going forward
|
||||
currentTargetPoint = transform.position + transform.forward * 100f;
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveTowardsTarget()
|
||||
{
|
||||
Vector3 direction = (currentTargetPoint - transform.position).normalized;
|
||||
Vector3 movement = direction * speed * Time.deltaTime;
|
||||
|
||||
transform.position += movement;
|
||||
|
||||
// Face movement direction
|
||||
if (movement.sqrMagnitude > 0.0001f)
|
||||
{
|
||||
transform.rotation = Quaternion.LookRotation(movement.normalized);
|
||||
}
|
||||
|
||||
// Check if we've arrived (only when locked)
|
||||
if (_isLocked && Vector3.Distance(transform.position, currentTargetPoint) <= arriveEpsilon)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[MeteorProjectile] Arrived at target");
|
||||
DoImpact(transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckCollisions()
|
||||
{
|
||||
// Use OverlapSphere for collision detection
|
||||
int hitCount = Physics.OverlapSphereNonAlloc(transform.position, collisionRadius, _overlapCache, stopOnLayers, QueryTriggerInteraction.Ignore);
|
||||
|
||||
if (hitCount > 0)
|
||||
{
|
||||
if (enableDebug) Debug.Log($"[MeteorProjectile] Collision detected with {_overlapCache[0].name}");
|
||||
DoImpact(transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Targeting & Movement
|
||||
|
||||
#region Impact & Damage
|
||||
|
||||
/// <summary>
|
||||
/// Unified impact handler (idempotent). Deals single-target damage if applicable,
|
||||
/// optional AOE damage, invokes events, and despawns.
|
||||
/// </summary>
|
||||
private void Impact(Collider hitCol, Vector3 hitPoint, Vector3 hitNormal)
|
||||
private void DoImpact(Vector3 impactPos)
|
||||
{
|
||||
if (hasImpacted) return;
|
||||
hasImpacted = true;
|
||||
if (_hasImpacted) return;
|
||||
_hasImpacted = true;
|
||||
|
||||
if (enableDebug)
|
||||
Debug.Log($"[Meteor] Impact with {(hitCol ? hitCol.name : "null")} at {hitPoint}");
|
||||
if (enableDebug) Debug.Log($"[MeteorProjectile] Impact at {impactPos}");
|
||||
|
||||
// Single target (original logic)
|
||||
if (!hasDealtDamage && hitCol != null && hitCol.CompareTag(targetTag))
|
||||
DealDamageToTarget(hitCol);
|
||||
|
||||
// AOE (if enabled)
|
||||
if (explosionRadius > 0f)
|
||||
DealAreaDamage(hitPoint);
|
||||
|
||||
// Hook for VFX/SFX/CameraShake
|
||||
onImpact?.Invoke();
|
||||
|
||||
Despawn();
|
||||
// Spawn VFX
|
||||
if (impactVfxPrefab != null)
|
||||
{
|
||||
var vfx = LeanPool.Spawn(impactVfxPrefab, impactPos, Quaternion.identity);
|
||||
// Auto-despawn VFX after 5 seconds
|
||||
LeanPool.Despawn(vfx, 5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deals damage to the intended single target using Invector interfaces/components.
|
||||
/// </summary>
|
||||
private void DealDamageToTarget(Collider targetCollider)
|
||||
onImpact?.Invoke();
|
||||
|
||||
// Deal area damage
|
||||
int damageTargets = Physics.OverlapSphereNonAlloc(impactPos, explosionRadius, _overlapCache, damageLayers, QueryTriggerInteraction.Ignore);
|
||||
|
||||
for (int i = 0; i < damageTargets; i++)
|
||||
{
|
||||
if (enableDebug) Debug.Log($"[MeteorDamage] Dealing {damage} damage to: {targetCollider.name}");
|
||||
var col = _overlapCache[i];
|
||||
if (col != null)
|
||||
{
|
||||
DealDamageToTarget(col, impactPos);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 hitPoint = GetClosestPointOnCollider(targetCollider);
|
||||
Vector3 hitDirection = GetHitDirection(targetCollider);
|
||||
// Despawn meteor
|
||||
LeanPool.Despawn(gameObject);
|
||||
}
|
||||
|
||||
vDamage damageInfo = new vDamage(Mathf.RoundToInt(damage))
|
||||
private void DealDamageToTarget(Collider targetCollider, Vector3 hitPoint)
|
||||
{
|
||||
Vector3 hitDirection = (targetCollider.bounds.center - hitPoint).normalized;
|
||||
if (hitDirection.sqrMagnitude < 0.0001f) hitDirection = Vector3.up;
|
||||
|
||||
vDamage damageInfo = new vDamage(damage)
|
||||
{
|
||||
sender = transform,
|
||||
hitPosition = hitPoint
|
||||
@@ -288,185 +297,104 @@ namespace DemonBoss.Magic
|
||||
|
||||
bool damageDealt = false;
|
||||
|
||||
// vIDamageReceiver
|
||||
// Try vIDamageReceiver first
|
||||
var receiver = targetCollider.GetComponent<vIDamageReceiver>() ??
|
||||
targetCollider.GetComponentInParent<vIDamageReceiver>();
|
||||
if (receiver != null)
|
||||
if (receiver != null && !damageDealt)
|
||||
{
|
||||
receiver.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
hasDealtDamage = true;
|
||||
if (enableDebug) Debug.Log("[MeteorDamage] Damage via vIDamageReceiver");
|
||||
}
|
||||
|
||||
// vHealthController
|
||||
// Fallback to vHealthController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var health = targetCollider.GetComponent<vHealthController>() ??
|
||||
var hc = targetCollider.GetComponent<vHealthController>() ??
|
||||
targetCollider.GetComponentInParent<vHealthController>();
|
||||
if (health != null)
|
||||
if (hc != null)
|
||||
{
|
||||
health.TakeDamage(damageInfo);
|
||||
hc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
hasDealtDamage = true;
|
||||
if (enableDebug) Debug.Log("[MeteorDamage] Damage via vHealthController");
|
||||
}
|
||||
}
|
||||
|
||||
// vThirdPersonController (including Beyond variant)
|
||||
// Fallback to vThirdPersonController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var tpc = targetCollider.GetComponent<vThirdPersonController>() ??
|
||||
targetCollider.GetComponentInParent<vThirdPersonController>();
|
||||
if (tpc != null)
|
||||
{
|
||||
// Handle Beyond variant
|
||||
if (tpc is Beyond.bThirdPersonController beyond)
|
||||
{
|
||||
if (!beyond.GodMode && !beyond.isImmortal)
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
hasDealtDamage = true;
|
||||
if (enableDebug) Debug.Log("[MeteorDamage] Damage via bThirdPersonController");
|
||||
}
|
||||
else if (enableDebug)
|
||||
{
|
||||
Debug.Log("[MeteorDamage] Target is immortal/GodMode – no damage");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
hasDealtDamage = true;
|
||||
if (enableDebug) Debug.Log("[MeteorDamage] Damage via vThirdPersonController");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!damageDealt && enableDebug)
|
||||
Debug.LogWarning("[MeteorDamage] No valid damage receiver found!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deals AOE damage to all colliders within explosionRadius using Invector-compatible logic.
|
||||
/// </summary>
|
||||
private void DealAreaDamage(Vector3 center)
|
||||
// Apply physics force
|
||||
var rb = targetCollider.attachedRigidbody;
|
||||
if (rb != null && knockbackForce > 0f)
|
||||
{
|
||||
Collider[] hits = Physics.OverlapSphere(center, explosionRadius, explosionMask, QueryTriggerInteraction.Ignore);
|
||||
foreach (var col in hits)
|
||||
{
|
||||
if (col == null) continue;
|
||||
if (aoeOnlyTargetTag && !col.CompareTag(targetTag)) continue;
|
||||
|
||||
// Avoid double-hitting the same single target if already processed
|
||||
if (col.CompareTag(targetTag) && hasDealtDamage) continue;
|
||||
|
||||
Vector3 hp = col.ClosestPoint(center);
|
||||
Vector3 dir = (col.bounds.center - center).normalized;
|
||||
|
||||
vDamage damageInfo = new vDamage(Mathf.RoundToInt(damage))
|
||||
{
|
||||
sender = transform,
|
||||
hitPosition = hp,
|
||||
force = knockbackForce > 0f ? dir * knockbackForce : Vector3.zero
|
||||
};
|
||||
|
||||
bool dealt = false;
|
||||
|
||||
var receiver = col.GetComponent<vIDamageReceiver>() ?? col.GetComponentInParent<vIDamageReceiver>();
|
||||
if (receiver != null) { receiver.TakeDamage(damageInfo); dealt = true; }
|
||||
|
||||
if (!dealt)
|
||||
{
|
||||
var health = col.GetComponent<vHealthController>() ?? col.GetComponentInParent<vHealthController>();
|
||||
if (health != null) { health.TakeDamage(damageInfo); dealt = true; }
|
||||
rb.AddForce(hitDirection * knockbackForce, ForceMode.Impulse);
|
||||
}
|
||||
|
||||
if (!dealt)
|
||||
if (enableDebug && damageDealt)
|
||||
{
|
||||
var tpc = col.GetComponent<vThirdPersonController>() ?? col.GetComponentInParent<vThirdPersonController>();
|
||||
if (tpc != null)
|
||||
{
|
||||
if (tpc is Beyond.bThirdPersonController beyond)
|
||||
{
|
||||
if (!beyond.GodMode && !beyond.isImmortal) { tpc.TakeDamage(damageInfo); dealt = true; }
|
||||
Debug.Log($"[MeteorProjectile] Dealt {damage} damage to {targetCollider.name}");
|
||||
}
|
||||
else { tpc.TakeDamage(damageInfo); dealt = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consider AOE as the final damage application for this projectile
|
||||
hasDealtDamage = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest point on a collider to this projectile's position.
|
||||
/// </summary>
|
||||
private Vector3 GetClosestPointOnCollider(Collider col)
|
||||
{
|
||||
return col.ClosestPoint(transform.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a hit direction from projectile toward a collider's center; falls back to current velocity.
|
||||
/// </summary>
|
||||
private Vector3 GetHitDirection(Collider col)
|
||||
{
|
||||
Vector3 dir = (col.bounds.center - transform.position).normalized;
|
||||
return dir.sqrMagnitude > 0.0001f ? dir : SafeNormal(velocity);
|
||||
}
|
||||
|
||||
#endregion Impact & Damage
|
||||
|
||||
#region Pooling
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Returns this projectile to the LeanPool.
|
||||
/// </summary>
|
||||
private void Despawn()
|
||||
private Vector3 SnapToGround(Vector3 point)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[Meteor] Despawn via LeanPool");
|
||||
LeanPool.Despawn(gameObject);
|
||||
Vector3 rayStart = point + Vector3.up * 10f;
|
||||
if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, 50f, groundMask, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
return hit.point;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
#endregion Pooling
|
||||
#endregion Helpers
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Editor-only gizmos to visualize entry direction and impact point when selected.
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(transform.position, 0.25f);
|
||||
|
||||
// Draw collision sphere
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawLine(transform.position, transform.position + SafeNormal(velocity.sqrMagnitude > 0 ? velocity : velocityDir) * 3f);
|
||||
Gizmos.DrawWireSphere(transform.position, collisionRadius);
|
||||
|
||||
// Draw explosion radius
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, explosionRadius);
|
||||
|
||||
// Draw target point
|
||||
if (currentTargetPoint != Vector3.zero)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(impactPoint, 0.35f);
|
||||
Gizmos.DrawWireSphere(currentTargetPoint, 0.5f);
|
||||
Gizmos.DrawLine(transform.position, currentTargetPoint);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#region Validation
|
||||
|
||||
/// <summary>
|
||||
/// Clamps and validates configuration values in the inspector.
|
||||
/// </summary>
|
||||
private void OnValidate()
|
||||
{
|
||||
speed = Mathf.Max(0f, speed);
|
||||
maxLifeTime = Mathf.Max(0.01f, maxLifeTime);
|
||||
explosionRadius = Mathf.Max(0f, explosionRadius);
|
||||
castRadius = Mathf.Clamp(castRadius, 0.01f, 2f);
|
||||
entryAngleDownFromHorizontal = Mathf.Clamp(entryAngleDownFromHorizontal, 0f, 89f);
|
||||
azimuthAimWeight = Mathf.Clamp01(azimuthAimWeight);
|
||||
}
|
||||
|
||||
#endregion Validation
|
||||
}
|
||||
public sealed class ReadOnlyInInspectorAttribute : PropertyAttribute
|
||||
{ }
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
using Invector.vCharacterController.AI.FSMBehaviour;
|
||||
using Lean.Pool;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DemonBoss.Magic
|
||||
{
|
||||
/// <summary>
|
||||
/// FSM Action: Boss calls down a meteor.
|
||||
/// Shows a decal at the player's position, locks an impact point on ground,
|
||||
/// then spawns the MeteorProjectile prefab above that point after a delay.
|
||||
/// Cancels cleanly on state exit.
|
||||
/// Spawns a meteor behind the BOSS and launches it toward the player's position
|
||||
/// Similar mechanics to FireballProjectile but coming from above
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Actions/DemonBoss/Call Meteor")]
|
||||
public class SA_CallMeteor : vStateAction
|
||||
@@ -21,124 +18,88 @@ namespace DemonBoss.Magic
|
||||
[Tooltip("Prefab with MeteorProjectile component")]
|
||||
public GameObject meteorPrefab;
|
||||
|
||||
[Tooltip("Visual decal prefab marking impact point")]
|
||||
public GameObject decalPrefab;
|
||||
[Tooltip("Distance behind the BOSS to spawn meteor (meters)")]
|
||||
public float behindBossDistance = 3f;
|
||||
|
||||
[Tooltip("Height above ground at which meteor spawns")]
|
||||
public float spawnHeight = 40f;
|
||||
[Tooltip("Height above the BOSS to spawn meteor (meters)")]
|
||||
public float aboveBossHeight = 8f;
|
||||
|
||||
[Tooltip("Delay before meteor spawns after decal")]
|
||||
public float castDelay = 1.5f;
|
||||
[Tooltip("Delay before meteor spawns (wind-up)")]
|
||||
public float castDelay = 0.4f;
|
||||
|
||||
[Header("Ground")]
|
||||
[Tooltip("Layer mask for ground raycast")]
|
||||
public LayerMask groundMask = -1;
|
||||
[Header("Targeting")]
|
||||
[Tooltip("Tag used to find the target (usually Player)")]
|
||||
public string targetTag = "Player";
|
||||
|
||||
[Header("Debug")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
private Transform player;
|
||||
private GameObject spawnedDecal;
|
||||
private Vector3 impactPoint;
|
||||
private Transform _boss;
|
||||
private Transform _target;
|
||||
|
||||
private Coroutine _spawnRoutine;
|
||||
private CoroutineRunner _runner;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the FSM action, delegates to OnEnter/OnExit depending on execution type.
|
||||
/// </summary>
|
||||
public override void DoAction(vIFSMBehaviourController fsm, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
|
||||
public override void DoAction(vIFSMBehaviourController fsm, vFSMComponentExecutionType execType = vFSMComponentExecutionType.OnStateUpdate)
|
||||
{
|
||||
if (execType == vFSMComponentExecutionType.OnStateEnter)
|
||||
{
|
||||
if (executionType == vFSMComponentExecutionType.OnStateEnter)
|
||||
OnEnter(fsm);
|
||||
|
||||
if (executionType == vFSMComponentExecutionType.OnStateExit)
|
||||
OnExit();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquires the player, locks the impact point on the ground, shows the decal,
|
||||
/// and starts the delayed meteor spawn coroutine.
|
||||
/// </summary>
|
||||
private void OnEnter(vIFSMBehaviourController fsm)
|
||||
{
|
||||
player = GameObject.FindGameObjectWithTag("Player")?.transform;
|
||||
if (player == null)
|
||||
_boss = fsm.transform;
|
||||
_target = GameObject.FindGameObjectWithTag(targetTag)?.transform;
|
||||
|
||||
if (_target == null)
|
||||
{
|
||||
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] No player found!");
|
||||
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] No target found – abort");
|
||||
return;
|
||||
}
|
||||
|
||||
// Raycast down from the player to lock the impact point
|
||||
Vector3 rayStart = player.position + Vector3.up * 5f;
|
||||
if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, 100f, groundMask, QueryTriggerInteraction.Ignore))
|
||||
impactPoint = hit.point;
|
||||
else
|
||||
impactPoint = player.position;
|
||||
if (enableDebug) Debug.Log($"[SA_CallMeteor] Boss: {_boss.name}, Target: {_target.name}");
|
||||
|
||||
// Spawn decal
|
||||
if (decalPrefab != null)
|
||||
spawnedDecal = LeanPool.Spawn(decalPrefab, impactPoint, Quaternion.identity);
|
||||
|
||||
// Get or add a dedicated runner for coroutines
|
||||
var hostGO = fsm.transform.gameObject;
|
||||
_runner = hostGO.GetComponent<CoroutineRunner>();
|
||||
if (_runner == null) _runner = hostGO.AddComponent<CoroutineRunner>();
|
||||
|
||||
// Start delayed spawn
|
||||
_spawnRoutine = _runner.StartCoroutine(SpawnMeteorAfterDelay());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the pending spawn and cleans up the decal when exiting the state.
|
||||
/// </summary>
|
||||
private void OnExit()
|
||||
{
|
||||
if (_runner != null && _spawnRoutine != null)
|
||||
{
|
||||
_runner.StopCoroutine(_spawnRoutine);
|
||||
_spawnRoutine = null;
|
||||
}
|
||||
|
||||
if (spawnedDecal != null)
|
||||
{
|
||||
LeanPool.Despawn(spawnedDecal);
|
||||
spawnedDecal = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the configured cast delay and then spawns the meteor.
|
||||
/// </summary>
|
||||
private IEnumerator SpawnMeteorAfterDelay()
|
||||
{
|
||||
yield return new WaitForSeconds(castDelay);
|
||||
SpawnMeteor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the meteor prefab above the locked impact point and cleans up the decal.
|
||||
/// </summary>
|
||||
private void SpawnMeteor()
|
||||
{
|
||||
if (meteorPrefab == null) return;
|
||||
|
||||
Vector3 spawnPos = impactPoint + Vector3.up * spawnHeight;
|
||||
LeanPool.Spawn(meteorPrefab, spawnPos, Quaternion.identity);
|
||||
|
||||
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor spawned at {spawnPos}, impact={impactPoint}");
|
||||
|
||||
if (spawnedDecal != null)
|
||||
if (meteorPrefab == null)
|
||||
{
|
||||
LeanPool.Despawn(spawnedDecal);
|
||||
spawnedDecal = null;
|
||||
}
|
||||
}
|
||||
if (enableDebug) Debug.LogError("[SA_CallMeteor] Missing meteorPrefab");
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight helper component dedicated to running coroutines for ScriptableObject actions.
|
||||
/// </summary>
|
||||
public sealed class CoroutineRunner : MonoBehaviour
|
||||
{ }
|
||||
if (_boss == null || _target == null)
|
||||
{
|
||||
if (enableDebug) Debug.LogError("[SA_CallMeteor] Missing boss or target reference");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate spawn position: behind the BOSS + height
|
||||
Vector3 bossForward = _boss.forward.normalized;
|
||||
Vector3 behindBoss = _boss.position - (bossForward * behindBossDistance);
|
||||
Vector3 spawnPos = behindBoss + Vector3.up * aboveBossHeight;
|
||||
|
||||
if (enableDebug) Debug.Log($"[SA_CallMeteor] Spawning meteor at: {spawnPos}");
|
||||
|
||||
// Spawn the meteor
|
||||
var meteorGO = LeanPool.Spawn(meteorPrefab, spawnPos, Quaternion.identity);
|
||||
|
||||
// Configure the projectile to target the player
|
||||
var meteorScript = meteorGO.GetComponent<MeteorProjectile>();
|
||||
if (meteorScript != null)
|
||||
{
|
||||
// Set it to target the player's current position
|
||||
meteorScript.useOverrideImpactPoint = true;
|
||||
meteorScript.overrideImpactPoint = _target.position;
|
||||
meteorScript.snapImpactToGround = true;
|
||||
|
||||
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor configured to target: {_target.position}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enableDebug) Debug.LogError("[SA_CallMeteor] Meteor prefab missing MeteorProjectile component!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,140 +5,97 @@ using UnityEngine;
|
||||
namespace DemonBoss.Magic
|
||||
{
|
||||
/// <summary>
|
||||
/// StateAction for intelligent crystal turret spawning
|
||||
/// Searches for optimal position in 2-6m ring from boss, prefers "behind boss" relative to player
|
||||
/// Spawns exactly 3 crystal turrets around the opponent.
|
||||
/// Now with per-spawn randomness: global rotation offset and per-turret angle/radius jitter.
|
||||
/// Validates positions (ground/obstacles) and enforces minimum separation.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Actions/DemonBoss/Spawn Turret Smart")]
|
||||
[CreateAssetMenu(menuName = "Invector/FSM/Actions/DemonBoss/Spawn 3 Turrets Radial")]
|
||||
public class SA_SpawnTurretSmart : vStateAction
|
||||
{
|
||||
public override string categoryName => "DemonBoss/Magic";
|
||||
public override string defaultName => "Spawn Turret Smart";
|
||||
public override string defaultName => "Spawn 3 Turrets Radial";
|
||||
|
||||
[Header("Turret Configuration")]
|
||||
[Tooltip("Crystal prefab with CrystalShooterAI component")]
|
||||
public GameObject crystalPrefab;
|
||||
|
||||
[Tooltip("Minimum distance from boss for crystal spawn")]
|
||||
public float minSpawnDistance = 2f;
|
||||
|
||||
[Tooltip("Maximum distance from boss for crystal spawn")]
|
||||
public float maxSpawnDistance = 6f;
|
||||
|
||||
[Tooltip("Collision check radius when choosing position")]
|
||||
public float obstacleCheckRadius = 1f;
|
||||
|
||||
[Tooltip("Height above ground for raycast ground checking")]
|
||||
public float groundCheckHeight = 2f;
|
||||
|
||||
[Tooltip("Layer mask for obstacles")]
|
||||
public LayerMask obstacleLayerMask = -1;
|
||||
|
||||
[Tooltip("Layer mask for ground")]
|
||||
public LayerMask groundLayerMask = -1;
|
||||
|
||||
[Tooltip("Animator bool parameter name for blocking state")]
|
||||
public string animatorBlockingBool = "IsBlocking";
|
||||
|
||||
[Header("Smart Positioning")]
|
||||
[Tooltip("Preference multiplier for positions behind boss (relative to player)")]
|
||||
public float backPreferenceMultiplier = 2f;
|
||||
[Header("Placement Search (Validation)")]
|
||||
public int perTurretAdjustmentTries = 10;
|
||||
|
||||
[Tooltip("Number of attempts to find valid position")]
|
||||
public int maxSpawnAttempts = 12;
|
||||
public float maxAngleAdjust = 25f;
|
||||
public float maxRadiusAdjust = 1.0f;
|
||||
public float minSeparationBetweenTurrets = 1.5f;
|
||||
|
||||
[Header("Randomization (Formation)")]
|
||||
[Tooltip("Random global rotation offset (degrees) applied to the 120° spokes.")]
|
||||
public bool globalStartAngleRandom = true;
|
||||
|
||||
[Tooltip("Per-turret angle jitter around the spoke (degrees). 0 = disabled.")]
|
||||
public float perTurretAngleJitter = 10f;
|
||||
|
||||
[Tooltip("Per-turret radius jitter (meters). 0 = disabled.")]
|
||||
public float perTurretRadiusJitter = 0.75f;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logging")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
[Tooltip("Show gizmos in Scene View")]
|
||||
public bool showGizmos = true;
|
||||
|
||||
private GameObject spawnedCrystal;
|
||||
private Animator npcAnimator;
|
||||
private Transform npcTransform;
|
||||
private Transform playerTransform;
|
||||
|
||||
/// <summary>
|
||||
/// Main action execution method called by FSM
|
||||
/// </summary>
|
||||
private readonly System.Collections.Generic.List<Vector3> _lastPlanned = new System.Collections.Generic.List<Vector3>(3);
|
||||
|
||||
public override void DoAction(vIFSMBehaviourController fsmBehaviour, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
|
||||
{
|
||||
if (executionType == vFSMComponentExecutionType.OnStateEnter)
|
||||
{
|
||||
OnStateEnter(fsmBehaviour);
|
||||
}
|
||||
else if (executionType == vFSMComponentExecutionType.OnStateExit)
|
||||
{
|
||||
OnStateExit(fsmBehaviour);
|
||||
}
|
||||
if (executionType == vFSMComponentExecutionType.OnStateEnter) OnStateEnter(fsmBehaviour);
|
||||
else if (executionType == vFSMComponentExecutionType.OnStateExit) OnStateExit(fsmBehaviour);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when entering state - intelligently spawns crystal
|
||||
/// </summary>
|
||||
private void OnStateEnter(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[SA_SpawnTurretSmart] Starting intelligent crystal spawn");
|
||||
|
||||
// Store NPC references
|
||||
npcTransform = fsmBehaviour.transform;
|
||||
npcAnimator = npcTransform.GetComponent<Animator>();
|
||||
|
||||
FindPlayer(fsmBehaviour);
|
||||
|
||||
if (npcAnimator != null && !string.IsNullOrEmpty(animatorBlockingBool))
|
||||
{
|
||||
npcAnimator.SetBool(animatorBlockingBool, true);
|
||||
if (enableDebug) Debug.Log($"[SA_SpawnTurretSmart] Set bool: {animatorBlockingBool} = true");
|
||||
}
|
||||
|
||||
SpawnCrystalSmart(fsmBehaviour);
|
||||
SpawnThreeTurretsRadial(fsmBehaviour);
|
||||
|
||||
DEC_CheckCooldown.SetCooldownStatic(fsmBehaviour, "Turret", 12f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when exiting state - cleanup
|
||||
/// </summary>
|
||||
private void OnStateExit(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
if (enableDebug) Debug.Log("[SA_SpawnTurretSmart] Exiting turret spawn state");
|
||||
|
||||
if (npcAnimator != null && !string.IsNullOrEmpty(animatorBlockingBool))
|
||||
{
|
||||
npcAnimator.SetBool(animatorBlockingBool, false);
|
||||
if (enableDebug) Debug.Log($"[SA_SpawnTurretSmart] Set bool: {animatorBlockingBool} = false");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds player transform
|
||||
/// </summary>
|
||||
private void FindPlayer(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
GameObject player = GameObject.FindGameObjectWithTag("Player");
|
||||
if (player != null)
|
||||
{
|
||||
playerTransform = player.transform;
|
||||
if (enableDebug) Debug.Log("[SA_SpawnTurretSmart] Player found by tag");
|
||||
return;
|
||||
}
|
||||
|
||||
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
|
||||
if (aiController != null && aiController.currentTarget != null)
|
||||
{
|
||||
playerTransform = aiController.currentTarget.transform;
|
||||
if (enableDebug) Debug.Log("[SA_SpawnTurretSmart] Player found through AI target");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enableDebug) Debug.LogWarning("[SA_SpawnTurretSmart] Player not found!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intelligently spawns crystal in optimal position
|
||||
/// </summary>
|
||||
private void SpawnCrystalSmart(vIFSMBehaviourController fsmBehaviour)
|
||||
private void SpawnThreeTurretsRadial(vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
if (crystalPrefab == null)
|
||||
{
|
||||
@@ -146,63 +103,85 @@ namespace DemonBoss.Magic
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 bestPosition = Vector3.zero;
|
||||
bool foundValidPosition = false;
|
||||
float bestScore = float.MinValue;
|
||||
_lastPlanned.Clear();
|
||||
|
||||
Vector3 bossPos = npcTransform.position;
|
||||
Vector3 playerDirection = Vector3.zero;
|
||||
Vector3 center = playerTransform != null ? playerTransform.position : npcTransform.position;
|
||||
float baseRadius = Mathf.Clamp((minSpawnDistance + maxSpawnDistance) * 0.5f, minSpawnDistance, maxSpawnDistance);
|
||||
|
||||
if (playerTransform != null)
|
||||
Vector3 refDir = Vector3.forward;
|
||||
if (playerTransform != null && npcTransform != null)
|
||||
{
|
||||
playerDirection = (playerTransform.position - bossPos).normalized;
|
||||
Vector3 d = (npcTransform.position - center); d.y = 0f;
|
||||
if (d.sqrMagnitude > 0.0001f) refDir = d.normalized;
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxSpawnAttempts; i++)
|
||||
else if (npcTransform != null)
|
||||
{
|
||||
float angle = (360f / maxSpawnAttempts) * i + Random.Range(-15f, 15f);
|
||||
Vector3 direction = new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0, Mathf.Sin(angle * Mathf.Deg2Rad));
|
||||
refDir = npcTransform.forward;
|
||||
}
|
||||
|
||||
float distance = Random.Range(minSpawnDistance, maxSpawnDistance);
|
||||
Vector3 testPosition = bossPos + direction * distance;
|
||||
float globalOffset = globalStartAngleRandom ? Random.Range(0f, 360f) : 0f;
|
||||
|
||||
if (IsPositionValid(testPosition, out Vector3 groundPosition))
|
||||
const int turretCount = 3;
|
||||
for (int i = 0; i < turretCount; i++)
|
||||
{
|
||||
float score = EvaluatePosition(groundPosition, playerDirection, direction, bossPos);
|
||||
float baseAngle = globalOffset + i * 120f;
|
||||
float angle = baseAngle + (perTurretAngleJitter > 0f ? Random.Range(-perTurretAngleJitter, perTurretAngleJitter) : 0f);
|
||||
|
||||
if (score > bestScore)
|
||||
float radius = baseRadius + (perTurretRadiusJitter > 0f ? Random.Range(-perTurretRadiusJitter, perTurretRadiusJitter) : 0f);
|
||||
radius = Mathf.Clamp(radius, minSpawnDistance, maxSpawnDistance);
|
||||
|
||||
Vector3 ideal = center + Quaternion.Euler(0f, angle, 0f) * refDir * radius;
|
||||
|
||||
Vector3? chosen = IsPositionValidAndSeparated(ideal)
|
||||
? (Vector3?)ideal
|
||||
: FindValidPositionAroundSpoke(center, refDir, angle, radius);
|
||||
|
||||
if (!chosen.HasValue)
|
||||
{
|
||||
bestScore = score;
|
||||
bestPosition = groundPosition;
|
||||
foundValidPosition = true;
|
||||
Vector3 rough = center + Quaternion.Euler(0f, angle, 0f) * refDir * radius;
|
||||
chosen = EnforceSeparationFallback(rough, center);
|
||||
}
|
||||
|
||||
Vector3 spawnPos = chosen.Value;
|
||||
_lastPlanned.Add(spawnPos);
|
||||
SpawnCrystal(spawnPos, fsmBehaviour);
|
||||
}
|
||||
}
|
||||
|
||||
if (foundValidPosition)
|
||||
private Vector3? FindValidPositionAroundSpoke(Vector3 center, Vector3 refDir, float baseAngleDeg, float desiredRadius)
|
||||
{
|
||||
SpawnCrystal(bestPosition, fsmBehaviour);
|
||||
if (enableDebug) Debug.Log($"[SA_SpawnTurretSmart] Crystal spawned at position: {bestPosition} (score: {bestScore:F2})");
|
||||
}
|
||||
else
|
||||
Vector3 ideal = center + Quaternion.Euler(0f, baseAngleDeg, 0f) * refDir * desiredRadius;
|
||||
if (IsPositionValidAndSeparated(ideal)) return ideal;
|
||||
|
||||
for (int t = 0; t < perTurretAdjustmentTries; t++)
|
||||
{
|
||||
Vector3 fallbackPos = bossPos + npcTransform.forward * minSpawnDistance;
|
||||
SpawnCrystal(fallbackPos, fsmBehaviour);
|
||||
if (enableDebug) Debug.LogWarning("[SA_SpawnTurretSmart] Using fallback position");
|
||||
float ang = baseAngleDeg + Random.Range(-maxAngleAdjust, maxAngleAdjust);
|
||||
float rad = Mathf.Clamp(desiredRadius + Random.Range(-maxRadiusAdjust, maxRadiusAdjust), minSpawnDistance, maxSpawnDistance);
|
||||
Vector3 cand = center + Quaternion.Euler(0f, ang, 0f) * refDir * rad;
|
||||
|
||||
if (IsPositionValidAndSeparated(cand)) return cand;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsPositionValidAndSeparated(Vector3 position)
|
||||
{
|
||||
if (!IsPositionValid(position, out Vector3 grounded)) return false;
|
||||
|
||||
for (int i = 0; i < _lastPlanned.Count; i++)
|
||||
{
|
||||
if (Vector3.Distance(_lastPlanned[i], grounded) < Mathf.Max(minSeparationBetweenTurrets, obstacleCheckRadius * 2f))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if position is valid (no obstacles, has ground)
|
||||
/// </summary>
|
||||
private bool IsPositionValid(Vector3 position, out Vector3 groundPosition)
|
||||
{
|
||||
groundPosition = position;
|
||||
|
||||
if (Physics.CheckSphere(position + Vector3.up * obstacleCheckRadius, obstacleCheckRadius, obstacleLayerMask))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Ray groundRay = new Ray(position + Vector3.up * groundCheckHeight, Vector3.down);
|
||||
if (Physics.Raycast(groundRay, out RaycastHit hit, groundCheckHeight + 2f, groundLayerMask))
|
||||
@@ -210,109 +189,82 @@ namespace DemonBoss.Magic
|
||||
groundPosition = hit.point;
|
||||
|
||||
if (Physics.CheckSphere(groundPosition + Vector3.up * obstacleCheckRadius, obstacleCheckRadius, obstacleLayerMask))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates position quality (higher score = better position)
|
||||
/// </summary>
|
||||
private float EvaluatePosition(Vector3 position, Vector3 playerDirection, Vector3 positionDirection, Vector3 bossPos)
|
||||
private Vector3 EnforceSeparationFallback(Vector3 desired, Vector3 center)
|
||||
{
|
||||
float score = 0f;
|
||||
|
||||
if (playerTransform != null && playerDirection != Vector3.zero)
|
||||
Vector3 candidate = desired;
|
||||
float step = 0.5f;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
float angleToPlayer = Vector3.Angle(-playerDirection, positionDirection);
|
||||
bool ok = true;
|
||||
for (int k = 0; k < _lastPlanned.Count; k++)
|
||||
{
|
||||
if (Vector3.Distance(_lastPlanned[k], candidate) < Mathf.Max(minSeparationBetweenTurrets, obstacleCheckRadius * 2f))
|
||||
{ ok = false; break; }
|
||||
}
|
||||
if (ok) return candidate;
|
||||
|
||||
// The smaller the angle (closer to "behind"), the better the score
|
||||
float backScore = (180f - angleToPlayer) / 180f;
|
||||
score += backScore * backPreferenceMultiplier;
|
||||
Vector3 dir = (candidate - center); dir.y = 0f;
|
||||
if (dir.sqrMagnitude < 0.0001f) dir = Vector3.right;
|
||||
candidate = center + dir.normalized * (dir.magnitude + step);
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
float distance = Vector3.Distance(position, bossPos);
|
||||
float optimalDistance = (minSpawnDistance + maxSpawnDistance) * 0.5f;
|
||||
float distanceScore = 1f - Mathf.Abs(distance - optimalDistance) / maxSpawnDistance;
|
||||
score += distanceScore;
|
||||
|
||||
score += Random.Range(-0.1f, 0.1f);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns crystal at given position
|
||||
/// </summary>
|
||||
private void SpawnCrystal(Vector3 position, vIFSMBehaviourController fsmBehaviour)
|
||||
{
|
||||
Quaternion rotation = Quaternion.identity;
|
||||
if (playerTransform != null)
|
||||
Transform lookAt = playerTransform != null ? playerTransform : npcTransform;
|
||||
|
||||
if (lookAt != null)
|
||||
{
|
||||
Vector3 lookDirection = (playerTransform.position - position).normalized;
|
||||
Vector3 lookDirection = (lookAt.position - position).normalized;
|
||||
lookDirection.y = 0;
|
||||
if (lookDirection != Vector3.zero)
|
||||
{
|
||||
rotation = Quaternion.LookRotation(lookDirection);
|
||||
}
|
||||
}
|
||||
|
||||
spawnedCrystal = LeanPool.Spawn(crystalPrefab, position, rotation);
|
||||
var spawned = LeanPool.Spawn(crystalPrefab, position, rotation);
|
||||
|
||||
var shooterAI = spawnedCrystal.GetComponent<CrystalShooterAI>();
|
||||
var shooterAI = spawned.GetComponent<CrystalShooterAI>();
|
||||
if (shooterAI == null)
|
||||
{
|
||||
Debug.LogError("[SA_SpawnTurretSmart] Crystal prefab doesn't have CrystalShooterAI component!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (playerTransform != null)
|
||||
else if (playerTransform != null)
|
||||
{
|
||||
shooterAI.SetTarget(playerTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws gizmos in Scene View for debugging
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!showGizmos) return;
|
||||
if (!showGizmos || npcTransform == null) return;
|
||||
|
||||
if (npcTransform != null)
|
||||
{
|
||||
Vector3 pos = npcTransform.position;
|
||||
Vector3 c = playerTransform ? playerTransform.position : npcTransform.position;
|
||||
|
||||
// Spawn ring
|
||||
Gizmos.color = Color.green;
|
||||
DrawWireCircle(pos, minSpawnDistance);
|
||||
DrawWireCircle(c, minSpawnDistance);
|
||||
Gizmos.color = Color.red;
|
||||
DrawWireCircle(pos, maxSpawnDistance);
|
||||
DrawWireCircle(c, maxSpawnDistance);
|
||||
|
||||
// Obstacle check radius
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(pos + Vector3.up * obstacleCheckRadius, obstacleCheckRadius);
|
||||
}
|
||||
Gizmos.color = Color.cyan;
|
||||
foreach (var p in _lastPlanned) Gizmos.DrawWireSphere(p + Vector3.up * 0.1f, 0.2f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for drawing circles
|
||||
/// </summary>
|
||||
private void DrawWireCircle(Vector3 center, float radius)
|
||||
{
|
||||
int segments = 32;
|
||||
float angle = 0f;
|
||||
Vector3 prevPoint = center + new Vector3(radius, 0, 0);
|
||||
|
||||
for (int i = 1; i <= segments; i++)
|
||||
{
|
||||
angle = (float)i / segments * 360f * Mathf.Deg2Rad;
|
||||
float angle = (float)i / segments * 360f * Mathf.Deg2Rad;
|
||||
Vector3 newPoint = center + new Vector3(Mathf.Cos(angle) * radius, 0, Mathf.Sin(angle) * radius);
|
||||
Gizmos.DrawLine(prevPoint, newPoint);
|
||||
prevPoint = newPoint;
|
||||
|
||||
191
Assets/AI/Demon/ShieldDamage.cs
Normal file
191
Assets/AI/Demon/ShieldDamage.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using Invector;
|
||||
using Invector.vCharacterController;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DemonBoss.Magic
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that adds damage when the player gets too close to the shield.
|
||||
/// Add to the shield object along with Sphere Collider (IsTrigger = true).
|
||||
/// </summary>
|
||||
public class ShieldDamage : MonoBehaviour
|
||||
{
|
||||
[Header("Damage Configuration")]
|
||||
[Tooltip("Damage dealt to the player")]
|
||||
public int damage = 15;
|
||||
|
||||
[Tooltip("Knockback force")]
|
||||
public float knockbackForce = 10f;
|
||||
|
||||
[Tooltip("Player Tag")]
|
||||
public string playerTag = "Player";
|
||||
|
||||
[Tooltip("Time between consecutive hits (so as not to deal damage to every frame)")]
|
||||
public float damageInterval = 1f;
|
||||
|
||||
[Header("Effects")]
|
||||
[Tooltip("Sound effect when dealing damage")]
|
||||
public AudioClip damageSound;
|
||||
|
||||
[Tooltip("Visual effect when dealing damage")]
|
||||
public GameObject damageEffect;
|
||||
|
||||
[Header("Debug")]
|
||||
[Tooltip("Enable debug logs")]
|
||||
public bool enableDebug = false;
|
||||
|
||||
private AudioSource audioSource;
|
||||
private float lastDamageTime;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (damageSound != null)
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.spatialBlend = 1f; // 3D sound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerStay(Collider other)
|
||||
{
|
||||
if (other.CompareTag(playerTag) && Time.time >= lastDamageTime + damageInterval)
|
||||
{
|
||||
DealDamageToPlayer(other);
|
||||
lastDamageTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
private void DealDamageToPlayer(Collider playerCollider)
|
||||
{
|
||||
if (enableDebug) Debug.Log($"[ShieldDamage] I deal {damage} damage to the player: {playerCollider.name}");
|
||||
|
||||
Vector3 hitPoint = playerCollider.ClosestPoint(transform.position);
|
||||
Vector3 hitDirection = (playerCollider.transform.position - transform.position).normalized;
|
||||
|
||||
vDamage damageInfo = new vDamage(damage)
|
||||
{
|
||||
sender = transform,
|
||||
hitPosition = hitPoint
|
||||
};
|
||||
|
||||
if (knockbackForce > 0f)
|
||||
{
|
||||
damageInfo.force = hitDirection * knockbackForce;
|
||||
}
|
||||
|
||||
bool damageDealt = false;
|
||||
|
||||
// 1) vIDamageReceiver
|
||||
var damageReceiver = playerCollider.GetComponent<vIDamageReceiver>() ??
|
||||
playerCollider.GetComponentInParent<vIDamageReceiver>();
|
||||
|
||||
if (damageReceiver != null)
|
||||
{
|
||||
damageReceiver.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ShieldDamage] Damage dealt by vIDamageReceiver");
|
||||
}
|
||||
|
||||
// 2) vHealthController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var healthController = playerCollider.GetComponent<vHealthController>() ??
|
||||
playerCollider.GetComponentInParent<vHealthController>();
|
||||
|
||||
if (healthController != null)
|
||||
{
|
||||
healthController.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ShieldDamage] Damage dealt by vHealthController");
|
||||
}
|
||||
}
|
||||
|
||||
// 3) vThirdPersonController
|
||||
if (!damageDealt)
|
||||
{
|
||||
var tpc = playerCollider.GetComponent<vThirdPersonController>() ??
|
||||
playerCollider.GetComponentInParent<vThirdPersonController>();
|
||||
|
||||
if (tpc != null)
|
||||
{
|
||||
// SprawdŸ czy to Beyond variant
|
||||
if (tpc is Beyond.bThirdPersonController beyond)
|
||||
{
|
||||
if (!beyond.GodMode && !beyond.isImmortal)
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ShieldDamage] Damage dealt by bThirdPersonController");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enableDebug) Debug.Log("[ShieldDamage] Player is immortal - no damage");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tpc.TakeDamage(damageInfo);
|
||||
damageDealt = true;
|
||||
if (enableDebug) Debug.Log("[ShieldDamage] Damage dealt by vThirdPersonController");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (damageDealt)
|
||||
{
|
||||
PlayEffects(hitPoint);
|
||||
}
|
||||
else if (enableDebug)
|
||||
{
|
||||
Debug.LogWarning("[ShieldDamage] Cannot deal damage - missing required component!");
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayEffects(Vector3 hitPoint)
|
||||
{
|
||||
// Play hit sound
|
||||
if (audioSource != null && damageSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(damageSound);
|
||||
}
|
||||
|
||||
// Show visual effect
|
||||
if (damageEffect != null)
|
||||
{
|
||||
GameObject effect = Instantiate(damageEffect, hitPoint, Quaternion.identity);
|
||||
Destroy(effect, 2f); // Remove after 2 seconds
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
/// <summary>
|
||||
/// Draw damage range visualization in Scene View
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Show damage range
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null && col.isTrigger)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
if (col is SphereCollider sphere)
|
||||
{
|
||||
Gizmos.DrawWireSphere(transform.position, sphere.radius);
|
||||
}
|
||||
else if (col is CapsuleCollider capsule)
|
||||
{
|
||||
// Approximate capsule visualization
|
||||
Gizmos.DrawWireSphere(transform.position, capsule.radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
2
Assets/AI/Demon/ShieldDamage.cs.meta
Normal file
2
Assets/AI/Demon/ShieldDamage.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a4b288b220c78b4ca00e13cb8a72d6b
|
||||
@@ -13,6 +13,9 @@ GameObject:
|
||||
- component: {fileID: 2323612}
|
||||
- component: {fileID: 13662188}
|
||||
- component: {fileID: 7020133711031364094}
|
||||
- component: {fileID: -8194063383422297065}
|
||||
- component: {fileID: 1536958584606674588}
|
||||
- component: {fileID: 8851143338676511289}
|
||||
m_Layer: 26
|
||||
m_Name: Turet
|
||||
m_TagString: Untagged
|
||||
@@ -104,7 +107,7 @@ CapsuleCollider:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_IsTrigger: 1
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
@@ -128,19 +131,73 @@ MonoBehaviour:
|
||||
fireballPrefab: {fileID: 1947871717301538, guid: 9591667a35466484096c6e63785e136c,
|
||||
type: 3}
|
||||
fireRate: 5
|
||||
maxShots: 3
|
||||
maxShots: 10
|
||||
despawnDelay: 10
|
||||
turnSpeed: 120
|
||||
idleSpinSpeed: 30
|
||||
aimTolerance: 5
|
||||
initialStaggerRange: {x: 0, y: 0.6}
|
||||
fireRateJitter: 0.5
|
||||
aimJitterDegrees: 0
|
||||
turnSpeed: 0
|
||||
idleSpinSpeed: 0
|
||||
aimTolerance: 360
|
||||
autoFindPlayer: 1
|
||||
playerTag: Player
|
||||
maxShootingRange: 50
|
||||
useShootEffects: 0
|
||||
useShootEffects: 1
|
||||
muzzleFlashPrefab: {fileID: 0}
|
||||
shootSound: {fileID: 8300000, guid: d44a96f293b66b74ea13a2e6fcc6c8fa, type: 3}
|
||||
enableDebug: 0
|
||||
shootSound: {fileID: 8300000, guid: f58578c3593cfcd40a4b21417e49421a, type: 3}
|
||||
enableDebug: 1
|
||||
showGizmos: 1
|
||||
--- !u!114 &-8194063383422297065
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 115880}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 06579ea47ceeddd42a05f7720c15b5de, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
maxHealth: 50
|
||||
currentHealth: 0
|
||||
destructionEffect: {fileID: 8876690725160639820, guid: c1b3da9fe585c44479dda2cd5c3a7b83,
|
||||
type: 3}
|
||||
destructionSound: {fileID: 8300000, guid: 3c1f8b87da670734991b73bf5c30f0af, type: 3}
|
||||
hitSound: {fileID: 8300000, guid: 0af332725d9792840a4caa29e8991d08, type: 3}
|
||||
damagedMaterial: {fileID: 2100000, guid: f94eea72c30ac2e45ab55894421ea48c, type: 2}
|
||||
damagedThreshold: 0.5
|
||||
enableDebug: 1
|
||||
--- !u!114 &1536958584606674588
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 115880}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 605ff11ee57b0c241a82c0c37c40c0bc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
openCloseEvents: 0
|
||||
openCloseWindow: 0
|
||||
selectedToolbar: 0
|
||||
messagesListeners: []
|
||||
--- !u!114 &8851143338676511289
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 115880}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 312629d966d1a40e3874daa93364e1f3, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
targetIcon: {fileID: 8317937118480752073, guid: 82581d6c5dd5945b89394d83db5f4c8b,
|
||||
type: 3}
|
||||
--- !u!1 &6534933121460188189
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -135,38 +135,6 @@ MonoBehaviour:
|
||||
shieldDuration: 10
|
||||
animatorBlockingBool: IsBlocking
|
||||
enableDebug: 1
|
||||
--- !u!114 &-7016508256595524775
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
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: af82d1d082ce4b9478d420f8ca1e72c2, type: 3}
|
||||
m_Name: Check Cooldown Meteor
|
||||
m_EditorClassIdentifier:
|
||||
parentFSM: {fileID: 11400000}
|
||||
editingName: 0
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
selectedFalse: 0
|
||||
cooldownKey: Meteor
|
||||
cooldownTime: 75
|
||||
availableAtStart: 1
|
||||
enableDebug: 0
|
||||
--- !u!114 &-6568372008305276654
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
@@ -190,17 +158,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: -368
|
||||
y: 311
|
||||
x: -35
|
||||
y: 290
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: -368, y: 311}
|
||||
positionRect: {x: -35, y: 290}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 0, g: 1, b: 1, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 0
|
||||
transitions:
|
||||
- decisions:
|
||||
@@ -221,14 +189,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -6568372008305276654}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -218
|
||||
y: 341
|
||||
x: 115
|
||||
y: 320
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -218
|
||||
y: 351
|
||||
x: 115
|
||||
y: 330
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -261,12 +229,10 @@ MonoBehaviour:
|
||||
editingName: 0
|
||||
meteorPrefab: {fileID: 1947871717301538, guid: f99aa3faf46a5f94985344f44aaf21aa,
|
||||
type: 3}
|
||||
decalPrefab: {fileID: 0}
|
||||
spawnHeight: 40
|
||||
behindBossDistance: 10
|
||||
aboveBossHeight: 20
|
||||
castDelay: 1.5
|
||||
groundMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
targetTag: Player
|
||||
enableDebug: 1
|
||||
--- !u!114 &-6379838510941931433
|
||||
MonoBehaviour:
|
||||
@@ -321,17 +287,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: -328
|
||||
y: 466
|
||||
x: 0
|
||||
y: 445
|
||||
width: 150
|
||||
height: 106
|
||||
positionRect: {x: -328, y: 466}
|
||||
positionRect: {x: 0, y: 445}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 0, b: 0, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 0
|
||||
transitions:
|
||||
- decisions:
|
||||
@@ -352,14 +318,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -6144582714324757854}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 496
|
||||
x: 150
|
||||
y: 475
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 506
|
||||
x: 150
|
||||
y: 485
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -380,14 +346,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -6144582714324757854}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 518
|
||||
x: 150
|
||||
y: 497
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 528
|
||||
x: 150
|
||||
y: 507
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -416,14 +382,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -6144582714324757854}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 540
|
||||
x: 150
|
||||
y: 519
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -178
|
||||
y: 550
|
||||
x: 150
|
||||
y: 529
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -574,17 +540,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: -48
|
||||
y: 316
|
||||
x: 280
|
||||
y: 295
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: -48, y: 316}
|
||||
positionRect: {x: 280, y: 295}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 0.10323405, g: 1, b: 0, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 1
|
||||
transitions:
|
||||
- decisions: []
|
||||
@@ -597,14 +563,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -3177478727897100882}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 102
|
||||
y: 346
|
||||
x: 430
|
||||
y: 325
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 102
|
||||
y: 356
|
||||
x: 430
|
||||
y: 335
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -643,17 +609,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 252
|
||||
y: 271
|
||||
x: 580
|
||||
y: 250
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: 252, y: 271}
|
||||
positionRect: {x: 580, y: 250}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 0, g: 1, b: 0.004989147, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 0
|
||||
transitions:
|
||||
- decisions:
|
||||
@@ -670,14 +636,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -2904979146780567904}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 242
|
||||
y: 301
|
||||
x: 570
|
||||
y: 280
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 402
|
||||
y: 311
|
||||
x: 730
|
||||
y: 290
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -778,8 +744,13 @@ MonoBehaviour:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
animatorBlockingBool: IsBlocking
|
||||
backPreferenceMultiplier: 2
|
||||
maxSpawnAttempts: 12
|
||||
perTurretAdjustmentTries: 10
|
||||
maxAngleAdjust: 25
|
||||
maxRadiusAdjust: 1
|
||||
minSeparationBetweenTurrets: 1.5
|
||||
globalStartAngleRandom: 1
|
||||
perTurretAngleJitter: 10
|
||||
perTurretRadiusJitter: 0.75
|
||||
enableDebug: 1
|
||||
showGizmos: 1
|
||||
--- !u!114 &-712571192746352845
|
||||
@@ -805,17 +776,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: -48
|
||||
y: 256
|
||||
x: 280
|
||||
y: 235
|
||||
width: 150
|
||||
height: 30
|
||||
positionRect: {x: -48, y: 256}
|
||||
positionRect: {x: 280, y: 235}
|
||||
rectWidth: 150
|
||||
editingName: 0
|
||||
nodeColor: {r: 0, g: 1, b: 0, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 0
|
||||
transitions: []
|
||||
actions: []
|
||||
@@ -881,27 +852,27 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: -243
|
||||
y: 726
|
||||
x: 85
|
||||
y: 705
|
||||
width: 150
|
||||
height: 150
|
||||
positionRect: {x: -243, y: 726}
|
||||
positionRect: {x: 85, y: 705}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 0.95132554, b: 0, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 1
|
||||
transitions:
|
||||
- decisions:
|
||||
- trueValue: 1
|
||||
decision: {fileID: 4031404829621142413}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
- trueValue: 1
|
||||
decision: {fileID: -2866484833343459521}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: 2986668563461644515}
|
||||
falseState: {fileID: 0}
|
||||
@@ -912,14 +883,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -312774025800194259}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 756
|
||||
x: 235
|
||||
y: 735
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 766
|
||||
x: 235
|
||||
y: 745
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -933,7 +904,7 @@ MonoBehaviour:
|
||||
- decisions:
|
||||
- trueValue: 0
|
||||
decision: {fileID: 4031404829621142413}
|
||||
isValid: 0
|
||||
isValid: 1
|
||||
validated: 0
|
||||
trueState: {fileID: -2904979146780567904}
|
||||
falseState: {fileID: 0}
|
||||
@@ -944,14 +915,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -312774025800194259}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 778
|
||||
x: 235
|
||||
y: 757
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 788
|
||||
x: 235
|
||||
y: 767
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -965,16 +936,16 @@ MonoBehaviour:
|
||||
- decisions:
|
||||
- trueValue: 1
|
||||
decision: {fileID: 4031404829621142413}
|
||||
isValid: 1
|
||||
validated: 0
|
||||
- trueValue: 0
|
||||
decision: {fileID: -2866484833343459521}
|
||||
isValid: 0
|
||||
validated: 0
|
||||
- trueValue: 0
|
||||
decision: {fileID: 7927421991537792917}
|
||||
decision: {fileID: -2866484833343459521}
|
||||
isValid: 1
|
||||
validated: 0
|
||||
- trueValue: 0
|
||||
decision: {fileID: 7927421991537792917}
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: -6144582714324757854}
|
||||
falseState: {fileID: 0}
|
||||
muteTrue: 0
|
||||
@@ -984,14 +955,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -312774025800194259}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -253
|
||||
y: 800
|
||||
x: 75
|
||||
y: 779
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 810
|
||||
x: 235
|
||||
y: 789
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1005,11 +976,11 @@ MonoBehaviour:
|
||||
- decisions:
|
||||
- trueValue: 1
|
||||
decision: {fileID: -3690511210373239573}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
- trueValue: 0
|
||||
decision: {fileID: 7927421991537792917}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: 0}
|
||||
falseState: {fileID: 0}
|
||||
@@ -1020,14 +991,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -312774025800194259}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 822
|
||||
x: 235
|
||||
y: 801
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 832
|
||||
x: 235
|
||||
y: 811
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1041,7 +1012,7 @@ MonoBehaviour:
|
||||
- decisions:
|
||||
- trueValue: 1
|
||||
decision: {fileID: 7927421991537792917}
|
||||
isValid: 0
|
||||
isValid: 1
|
||||
validated: 0
|
||||
trueState: {fileID: -2904979146780567904}
|
||||
falseState: {fileID: 0}
|
||||
@@ -1052,14 +1023,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: -312774025800194259}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 844
|
||||
x: 235
|
||||
y: 823
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: -93
|
||||
y: 854
|
||||
x: 235
|
||||
y: 833
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1106,7 +1077,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: a5fc604039227434d8b4e63ebc5e74a5, type: 3}
|
||||
m_Name: FSM_Demon
|
||||
m_EditorClassIdentifier:
|
||||
selectedNode: {fileID: 4162026404432437805}
|
||||
selectedNode: {fileID: 2986668563461644515}
|
||||
wantConnection: 0
|
||||
connectionNode: {fileID: 0}
|
||||
showProperties: 1
|
||||
@@ -1123,7 +1094,7 @@ MonoBehaviour:
|
||||
- {fileID: 9112689765763526057}
|
||||
- {fileID: 766956384951898899}
|
||||
- {fileID: 4162026404432437805}
|
||||
panOffset: {x: -410, y: 290}
|
||||
panOffset: {x: -795, y: 390}
|
||||
overNode: 0
|
||||
actions:
|
||||
- {fileID: 0}
|
||||
@@ -1207,17 +1178,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 772
|
||||
y: 956
|
||||
x: 1100
|
||||
y: 935
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: 772, y: 956}
|
||||
positionRect: {x: 1100, y: 935}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 1
|
||||
transitions:
|
||||
- decisions: []
|
||||
@@ -1230,14 +1201,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 762670965814380212}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 762
|
||||
y: 986
|
||||
x: 1090
|
||||
y: 965
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 922
|
||||
y: 996
|
||||
x: 1250
|
||||
y: 975
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1277,11 +1248,11 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 840
|
||||
y: 410
|
||||
x: 1165
|
||||
y: 385
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: 840, y: 410}
|
||||
positionRect: {x: 1165, y: 385}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
@@ -1300,14 +1271,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 766956384951898899}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 830
|
||||
y: 440
|
||||
x: 1155
|
||||
y: 415
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 990
|
||||
y: 450
|
||||
x: 1315
|
||||
y: 425
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1396,17 +1367,17 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 562
|
||||
y: 956
|
||||
x: 890
|
||||
y: 935
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: 562, y: 956}
|
||||
positionRect: {x: 890, y: 935}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 1
|
||||
transitions:
|
||||
- decisions: []
|
||||
@@ -1419,14 +1390,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2691300596403639167}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 552
|
||||
y: 986
|
||||
x: 880
|
||||
y: 965
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 712
|
||||
y: 996
|
||||
x: 1040
|
||||
y: 975
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1456,33 +1427,33 @@ MonoBehaviour:
|
||||
m_Name: Combat
|
||||
m_EditorClassIdentifier:
|
||||
description: FSM State
|
||||
selectedDecisionIndex: 0
|
||||
selectedDecisionIndex: 4
|
||||
canRemove: 1
|
||||
canTranstTo: 1
|
||||
canSetAsDefault: 1
|
||||
canEditName: 1
|
||||
canEditColor: 1
|
||||
isOpen: 1
|
||||
isSelected: 0
|
||||
isSelected: 1
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 307
|
||||
y: 676
|
||||
x: 635
|
||||
y: 655
|
||||
width: 150
|
||||
height: 150
|
||||
positionRect: {x: 307, y: 676}
|
||||
positionRect: {x: 635, y: 655}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 0, b: 0, a: 1}
|
||||
resizeLeft: 0
|
||||
resizeRight: 0
|
||||
inDrag: 1
|
||||
inDrag: 0
|
||||
resetCurrentDestination: 1
|
||||
transitions:
|
||||
- decisions:
|
||||
- trueValue: 1
|
||||
decision: {fileID: 7927421991537792917}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: -2904979146780567904}
|
||||
falseState: {fileID: 0}
|
||||
@@ -1493,14 +1464,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2986668563461644515}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 297
|
||||
y: 706
|
||||
x: 625
|
||||
y: 685
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 716
|
||||
x: 785
|
||||
y: 695
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1525,14 +1496,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2986668563461644515}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 297
|
||||
y: 728
|
||||
x: 625
|
||||
y: 707
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 738
|
||||
x: 785
|
||||
y: 717
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1561,14 +1532,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2986668563461644515}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 750
|
||||
x: 785
|
||||
y: 729
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 760
|
||||
x: 785
|
||||
y: 739
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1586,7 +1557,7 @@ MonoBehaviour:
|
||||
validated: 0
|
||||
- trueValue: 1
|
||||
decision: {fileID: 8113515040269600600}
|
||||
isValid: 1
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: 9112689765763526057}
|
||||
falseState: {fileID: 0}
|
||||
@@ -1597,14 +1568,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2986668563461644515}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 772
|
||||
x: 785
|
||||
y: 751
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 782
|
||||
x: 785
|
||||
y: 761
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
@@ -1618,11 +1589,11 @@ MonoBehaviour:
|
||||
- decisions:
|
||||
- trueValue: 0
|
||||
decision: {fileID: -6379838510941931433}
|
||||
isValid: 0
|
||||
isValid: 1
|
||||
validated: 0
|
||||
- trueValue: 1
|
||||
decision: {fileID: -7016508256595524775}
|
||||
isValid: 1
|
||||
decision: {fileID: 2998305265418220943}
|
||||
isValid: 0
|
||||
validated: 0
|
||||
trueState: {fileID: 4162026404432437805}
|
||||
falseState: {fileID: 0}
|
||||
@@ -1633,17 +1604,17 @@ MonoBehaviour:
|
||||
parentState: {fileID: 2986668563461644515}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 794
|
||||
x: 785
|
||||
y: 773
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 457
|
||||
y: 804
|
||||
x: 785
|
||||
y: 783
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
selectedTrue: 1
|
||||
selectedFalse: 0
|
||||
trueSideRight: 1
|
||||
falseSideRight: 1
|
||||
@@ -1657,6 +1628,38 @@ MonoBehaviour:
|
||||
useDecisions: 1
|
||||
parentGraph: {fileID: 11400000}
|
||||
defaultTransition: {fileID: 0}
|
||||
--- !u!114 &2998305265418220943
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
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: af82d1d082ce4b9478d420f8ca1e72c2, type: 3}
|
||||
m_Name: Check Cooldown Meteor
|
||||
m_EditorClassIdentifier:
|
||||
parentFSM: {fileID: 11400000}
|
||||
editingName: 0
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
selectedFalse: 0
|
||||
cooldownKey: Meteor
|
||||
cooldownTime: 100
|
||||
availableAtStart: 1
|
||||
enableDebug: 1
|
||||
--- !u!114 &4031404829621142413
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
@@ -1705,14 +1708,14 @@ MonoBehaviour:
|
||||
canEditName: 1
|
||||
canEditColor: 1
|
||||
isOpen: 0
|
||||
isSelected: 1
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 840
|
||||
y: 625
|
||||
x: 1160
|
||||
y: 600
|
||||
width: 150
|
||||
height: 30
|
||||
positionRect: {x: 840, y: 625}
|
||||
positionRect: {x: 1160, y: 600}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
@@ -1731,14 +1734,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 4162026404432437805}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 915
|
||||
y: 640
|
||||
x: 1235
|
||||
y: 615
|
||||
width: 0
|
||||
height: 0
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 915
|
||||
y: 640
|
||||
x: 1235
|
||||
y: 615
|
||||
width: 0
|
||||
height: 0
|
||||
selectedTrue: 0
|
||||
@@ -1862,7 +1865,7 @@ MonoBehaviour:
|
||||
cooldownKey: Shield
|
||||
cooldownTime: 75
|
||||
availableAtStart: 1
|
||||
enableDebug: 0
|
||||
enableDebug: 1
|
||||
--- !u!114 &8860036500635384459
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
@@ -1904,11 +1907,11 @@ MonoBehaviour:
|
||||
isSelected: 0
|
||||
nodeRect:
|
||||
serializedVersion: 2
|
||||
x: 845
|
||||
y: 230
|
||||
x: 1170
|
||||
y: 205
|
||||
width: 150
|
||||
height: 62
|
||||
positionRect: {x: 845, y: 230}
|
||||
positionRect: {x: 1170, y: 205}
|
||||
rectWidth: 150
|
||||
editingName: 1
|
||||
nodeColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
@@ -1927,14 +1930,14 @@ MonoBehaviour:
|
||||
parentState: {fileID: 9112689765763526057}
|
||||
trueRect:
|
||||
serializedVersion: 2
|
||||
x: 835
|
||||
y: 260
|
||||
x: 1160
|
||||
y: 235
|
||||
width: 10
|
||||
height: 10
|
||||
falseRect:
|
||||
serializedVersion: 2
|
||||
x: 995
|
||||
y: 270
|
||||
x: 1320
|
||||
y: 245
|
||||
width: 10
|
||||
height: 10
|
||||
selectedTrue: 0
|
||||
|
||||
Reference in New Issue
Block a user