magic powers refactor WIP

This commit is contained in:
2026-01-19 14:25:38 +01:00
parent f303bfc2a8
commit b988044526
35 changed files with 60438 additions and 59651 deletions

View File

@@ -0,0 +1,80 @@
using System.Collections;
using UnityEngine;
namespace Beyond
{
[CreateAssetMenu(menuName = "Magic/Spells/Duration Effect (Shield, Push, Flame)")]
public class DurationSpell : SpellDefinition
{
public enum DurationType { Simple, Shield, Flamethrower }
public DurationType type = DurationType.Simple;
public GameObject effectPrefab;
public float preCastDelay = 0f; // Matches 'startTime'
public float duration = 2f; // Matches 'endTime'
public float postEndDelay = 0f; // Matches 'delay'
public override void Cast(MagicAttacks caster, Transform target)
{
caster.StartCoroutine(CastRoutine(caster, target));
}
private IEnumerator CastRoutine(MagicAttacks caster, Transform target)
{
// 1. Rotation (Only Push and Flame used rotation in your old script)
if (type != DurationType.Shield)
{
yield return caster.RotateTowardsTargetRoutine(target, rotationDuration);
}
// 2. Pre-cast Delay
if (preCastDelay > 0) yield return new WaitForSeconds(preCastDelay);
// 3. Instantiate attached to player
GameObject instance = Instantiate(effectPrefab, caster.transform);
// Reset local position/rotation to ensure it aligns with player
instance.transform.localPosition = Vector3.zero;
instance.transform.localRotation = Quaternion.identity;
// 4. Initialize Specific Logic
if (type == DurationType.Shield)
{
var shieldCtrl = instance.GetComponent<ShieldEffectController>();
if (shieldCtrl) shieldCtrl.InitializeEffect();
}
else if (type == DurationType.Flamethrower)
{
var ps = instance.GetComponent<ParticleSystem>();
if (ps) ps.Play();
}
caster.ApplyDamageModifiers(instance);
// 5. Wait Duration (With Trinket Mods)
float finalDuration = duration;
if (type == DurationType.Shield && Player.Instance.CurrentTrinketStats.effectCalmness)
{
finalDuration *= 1.5f;
}
yield return new WaitForSeconds(finalDuration);
// 6. Cleanup Logic
if (type == DurationType.Flamethrower)
{
var ps = instance.GetComponent<ParticleSystem>();
if (ps) ps.Stop();
}
else if (type == DurationType.Shield)
{
var shieldCtrl = instance.GetComponent<ShieldEffectController>();
if (shieldCtrl) shieldCtrl.DisableEffect();
}
// 7. Post Delay
if (postEndDelay > 0) yield return new WaitForSeconds(postEndDelay);
Destroy(instance);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2fa1c233b63854f6d845066ccd8e48cd

View File

@@ -724,5 +724,57 @@ namespace Beyond
yield return new WaitForSeconds(fireballDamagerDuration);
collider.enabled = false;
}
//placeholder for future methods related to magic attacks
/// <summary>
/// Spells call this to rotate the player before firing
/// </summary>
public IEnumerator RotateTowardsTargetRoutine(Transform target, float duration)
{
if (target == null || duration <= 0) yield break;
float timer = 0f;
while (timer < duration)
{
if (target == null) yield break;
Vector3 dir = (target.position - transform.position);
dir.y = 0;
if (dir.sqrMagnitude > 0.01f)
{
Quaternion look = Quaternion.LookRotation(dir);
transform.rotation = Quaternion.RotateTowards(transform.rotation, look, Time.deltaTime * 500f); // Fast rotation
}
timer += Time.deltaTime;
yield return null;
}
}
/// <summary>
/// Helper to apply Trinket damage (Soulfire) to an object
/// </summary>
public void ApplyDamageModifiers(GameObject spellObject)
{
float mult = Player.Instance.CurrentTrinketStats.soulfireDamageMult;
if (Mathf.Abs(mult - 1f) < 0.01f) return;
var damages = spellObject.GetComponentsInChildren<Invector.vObjectDamage>();
foreach (var d in damages)
{
d.damage.damageValue = Mathf.RoundToInt(d.damage.damageValue * mult);
}
}
public bItem GetEquippedSpellItem()
{
if (powersArea != null && powersArea.equipSlots.Count > 0)
{
// Assuming the logic uses the current selection
// If powersArea tracks selection internally, use powersArea.currentEquippedItem
return powersArea.currentEquippedItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,46 @@
using System.Collections;
using UnityEngine;
using Invector;
namespace Beyond
{
[CreateAssetMenu(menuName = "Magic/Spells/Projectile Spell")]
public class ProjectileSpell : SpellDefinition
{
[Header("Projectile Settings")]
public GameObject projectilePrefab;
public float spawnDelay = 0.2f; // Sync with animation hand throw
public float lifeTime = 5f;
public float aimHeightOffset = 1.0f;
public override void Cast(MagicAttacks caster, Transform target)
{
caster.StartCoroutine(CastRoutine(caster, target));
}
private IEnumerator CastRoutine(MagicAttacks caster, Transform target)
{
// 1. Rotate
yield return caster.RotateTowardsTargetRoutine(target, rotationDuration);
// 2. Wait for animation point
yield return new WaitForSeconds(Mathf.Max(0, spawnDelay - rotationDuration));
// 3. Spawn
Vector3 spawnPos = caster.transform.position + caster.transform.forward + Vector3.up * 1.5f;
GameObject obj = Instantiate(projectilePrefab, spawnPos, caster.transform.rotation);
// 4. Aim
if (target != null)
{
Vector3 aimDir = (target.position + Vector3.up * aimHeightOffset) - spawnPos;
obj.transform.rotation = Quaternion.LookRotation(aimDir);
}
// 5. Apply Modifiers
caster.ApplyDamageModifiers(obj);
Destroy(obj, lifeTime);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ad80a581f715a4b9d9c0f9a0c1c42e64

View File

@@ -0,0 +1,52 @@
using System.Collections;
using UnityEngine;
using UnityEngine.VFX;
namespace Beyond
{
[CreateAssetMenu(menuName = "Magic/Spells/Scan")]
public class ScanSpell : SpellDefinition
{
public GameObject effectPrefab;
public float maxRange = 50f;
public float scanDuration = 2f; // time for wave to travel
public float startDelay = 0.6f;
public override void Cast(MagicAttacks caster, Transform target)
{
caster.StartCoroutine(CastRoutine(caster));
}
private IEnumerator CastRoutine(MagicAttacks caster)
{
yield return new WaitForSeconds(startDelay);
// Instantiate visual
GameObject instance = Instantiate(effectPrefab, caster.transform.position, Quaternion.identity);
VisualEffect vfx = instance.GetComponent<VisualEffect>();
if (vfx) vfx.Play();
// Shader Logic
float speed = maxRange / scanDuration;
float timer = 0f;
while (timer < scanDuration)
{
Shader.SetGlobalFloat("_WaveTime", speed * timer);
timer += Time.deltaTime;
yield return null;
}
Shader.SetGlobalFloat("_WaveTime", 0f);
// Detection Logic
int mask = 1 << LayerMask.NameToLayer("Triggers") | 1 << LayerMask.NameToLayer("HiddenObject");
var colliders = Physics.OverlapSphere(caster.transform.position, maxRange, mask);
foreach (var c in colliders)
{
var h = c.gameObject.GetComponent<IScannable>();
if (h != null) h.OnScanned();
}
Destroy(instance);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: af5ce789b2ab94e9bb1181148355aa91

View File

@@ -0,0 +1,64 @@
using System.Collections;
using UnityEngine;
namespace Beyond
{
[CreateAssetMenu(menuName = "Magic/Spells/Silent Peek")]
public class SilentPeekSpell : SpellDefinition
{
[Header("Timing")]
[Tooltip("Time to wait before toggling (Matches old 'Start Time')")]
public float startDelay = 0.85f;
// 1. Logic: Angel Eye makes this spell cost 0 Faith
public override float GetFaithCost(Player player)
{
if (player.CurrentTrinketStats.effectAngelEye)
{
return 0f;
}
return base.GetFaithCost(player);
}
public override void Cast(MagicAttacks caster, Transform target)
{
caster.StartCoroutine(CastRoutine(caster));
}
private IEnumerator CastRoutine(MagicAttacks caster)
{
// 2. Logic: Wait for the animation to reach the point (Start Time)
if (startDelay > 0) yield return new WaitForSeconds(startDelay);
if (SilentPeekController.instance != null)
{
if (SilentPeekController.instance.IsActive())
{
// Turn Off
SilentPeekController.instance.SetActive(false);
}
else
{
// Turn On
// A. Get the Item from the Caster (Requires the helper method added above)
bItem spellItem = caster.GetEquippedSpellItem();
// B. Handle Zora's Focus (Effect Breeze) logic
// (Placeholder: Your original code noted this was for future implementation)
if (Player.Instance.CurrentTrinketStats.effectBreeze)
{
// Example: Increase radius or duration here in the future
}
// C. Activate Controller
SilentPeekController.instance.SetActive(true, spellItem);
}
}
else
{
Debug.LogWarning("SilentPeekSpell: SilentPeekController Instance is null.");
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6cf5fce543d1c4a279a6d4571b819684

View File

@@ -0,0 +1,46 @@
using System.Collections;
using UnityEngine;
namespace Beyond
{
[CreateAssetMenu(menuName = "Magic/Spells/Spawn At Player Spell")]
public class SpawnAtPlayerSpell : SpellDefinition
{
public GameObject effectPrefab;
public float duration = 3f;
public float delay = 0.0f;
public bool attachToPlayer = true;
[Header("Trinket Special Interactions")]
public bool isShieldSpell; // Flag to apply Calmness
public override void Cast(MagicAttacks caster, Transform target)
{
caster.StartCoroutine(SpawnRoutine(caster));
}
private IEnumerator SpawnRoutine(MagicAttacks caster)
{
yield return new WaitForSeconds(delay);
GameObject instance;
if (attachToPlayer)
instance = Instantiate(effectPrefab, caster.transform);
else
instance = Instantiate(effectPrefab, caster.transform.position, caster.transform.rotation);
// Apply specific Trinket logic (Strategy pattern allows custom overrides too)
float finalDuration = duration;
if (isShieldSpell && Player.Instance.CurrentTrinketStats.effectCalmness)
{
finalDuration *= 1.5f;
}
caster.ApplyDamageModifiers(instance);
yield return new WaitForSeconds(finalDuration);
Destroy(instance);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f4842a395b80848e389ee3b6a5cc5e0c

View File

@@ -0,0 +1,43 @@
using UnityEngine;
using Sirenix.OdinInspector;
namespace Beyond
{
public abstract class SpellDefinition : ScriptableObject
{
[Header("General Settings")]
public string spellName;
public string animationClipName;
[TextArea] public string description;
[Header("Costs & Timing")]
public int baseFaithCost = 10;
public float castTime = 0.5f; // Time until the effect spawns (Animation event timing)
[Header("Targeting")]
public float rotationDuration = 0.3f; // How long player rotates towards target
/// <summary>
/// Calculates final cost based on Trinkets (Bloom, Angel Eye, etc)
/// </summary>
public virtual float GetFaithCost(Player player)
{
float cost = baseFaithCost;
// 1. Apply Global Reductions (e.g. Bloom)
if (player.CurrentTrinketStats.effectBloom)
{
cost *= 0.8f;
}
return cost;
}
/// <summary>
/// The logic for the specific spell.
/// </summary>
/// <param name="caster">The MonoBehavior running the spell (MagicAttacks)</param>
/// <param name="target">The current auto-target (can be null)</param>
public abstract void Cast(MagicAttacks caster, Transform target);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 296908ba0f0f14b4c8c57a84bf4bd5b2

View File

@@ -0,0 +1,21 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6cf5fce543d1c4a279a6d4571b819684, type: 3}
m_Name: Spell_CovertGaze
m_EditorClassIdentifier:
spellName: Covert Gaze
animationClipName: Fireball
description:
baseFaithCost: 10
castTime: 0.5
rotationDuration: 0.3
startDelay: 0.85

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 51e2575f2639b4c1980f4d657aac4507
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fa1c233b63854f6d845066ccd8e48cd, type: 3}
m_Name: Spell_QuantumBlast
m_EditorClassIdentifier:
spellName: Quantum Blast
animationClipName: MagicPush
description:
baseFaithCost: 10
castTime: 0.5
rotationDuration: 0
type: 0
effectPrefab: {fileID: 8935763087124063772, guid: c6853e03448db034b98691aaf1e5f95f,
type: 3}
preCastDelay: 0
duration: 5
postEndDelay: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 593b751fb37ef4e1693026f13ecfb5ff
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fa1c233b63854f6d845066ccd8e48cd, type: 3}
m_Name: Spell_QuantumShield
m_EditorClassIdentifier:
spellName: Quantum Shield
animationClipName: Shield
description:
baseFaithCost: 10
castTime: 0.5
rotationDuration: 0
type: 1
effectPrefab: {fileID: 4418269076189414613, guid: 4ae14a17e640b604f8b4921d230b0c96,
type: 3}
preCastDelay: 1
duration: 10
postEndDelay: 2

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e97e46ee2bee6459aad693bfa6680277
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ad80a581f715a4b9d9c0f9a0c1c42e64, type: 3}
m_Name: Spell_SparkOfJustice
m_EditorClassIdentifier:
spellName: Spark of Justice
animationClipName: Fireball
description:
baseFaithCost: 10
castTime: 0.5
rotationDuration: 0.85
projectilePrefab: {fileID: 11175768915115734, guid: fa8fab8e229636f44b7f773bd46aaf07,
type: 3}
spawnDelay: 0.85
lifeTime: 10
aimHeightOffset: 0.75

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0baead00b71554981b1681361e85af78
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: af5ce789b2ab94e9bb1181148355aa91, type: 3}
m_Name: Spell_SpectrumScan
m_EditorClassIdentifier:
spellName: Spectrum Scan
animationClipName: Scan
description:
baseFaithCost: 10
castTime: 0.5
rotationDuration: 0.3
effectPrefab: {fileID: 140570917138448771, guid: 03b028c9a91184b8bbbae340069578ae,
type: 3}
maxRange: 50
scanDuration: 4.4
startDelay: 0.6

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dc808b91d3d2c4c53873cfe6fc9f0305
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: