diff --git a/Assets/Data/Items/Vampire's draught.asset b/Assets/Data/Items/Vampire's draught.asset new file mode 100644 index 0000000..b698bf7 --- /dev/null +++ b/Assets/Data/Items/Vampire's draught.asset @@ -0,0 +1,74 @@ +%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: c437a5565f39495281bf1dd14dd6ba40, type: 3} + m_Name: Vampire's draught + m_EditorClassIdentifier: + serializationData: + SerializedFormat: 2 + SerializedBytes: + ReferencedUnityObjects: [] + SerializedBytesString: + Prefab: {fileID: 0} + PrefabModificationsReferencedUnityObjects: [] + PrefabModifications: [] + SerializationNodes: + - Name: cures + Entry: 7 + Data: 0|System.Collections.Generic.List`1[[Interfaces.IStatModifier, Assembly-CSharp]], + mscorlib + - Name: + Entry: 12 + Data: 1 + - Name: + Entry: 7 + Data: 1|Modifiers.HealOnKillModifier, Assembly-CSharp + - Name: value + Entry: 4 + Data: 10 + - Name: + Entry: 8 + Data: + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: + - Name: curses + Entry: 7 + Data: 2|System.Collections.Generic.List`1[[Interfaces.IStatModifier, Assembly-CSharp]], + mscorlib + - Name: + Entry: 12 + Data: 1 + - Name: + Entry: 7 + Data: 3|Modifiers.PercentStatModifier, Assembly-CSharp + - Name: stat + Entry: 3 + Data: 1 + - Name: percent + Entry: 4 + Data: -0.1 + - Name: + Entry: 8 + Data: + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: + itemName: "Vampire\u2019s Draught" + description: 'Cure: +10 Health on Kill, Curse: MaxHealth -10%' + icon: {fileID: 0} + price: 0 diff --git a/Assets/Data/Items/Vampire's draught.asset.meta b/Assets/Data/Items/Vampire's draught.asset.meta new file mode 100644 index 0000000..56ad25a --- /dev/null +++ b/Assets/Data/Items/Vampire's draught.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5366536d04e61959f911fa119df9eca4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/Enemies/Test Enemy.prefab b/Assets/Prefabs/Enemies/Test Enemy.prefab index 094bad4..fc1c08d 100644 --- a/Assets/Prefabs/Enemies/Test Enemy.prefab +++ b/Assets/Prefabs/Enemies/Test Enemy.prefab @@ -410,6 +410,7 @@ MonoBehaviour: character: {fileID: 7110610235440076017} deathBehavior: _implementer: {fileID: 4476814410094481518} + health: {fileID: 5572087310217837258} --- !u!114 &4476814410094481518 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/Weapons/PistolProjectile.prefab b/Assets/Prefabs/Weapons/PistolProjectile.prefab index 42a77b5..8cdf9bb 100644 --- a/Assets/Prefabs/Weapons/PistolProjectile.prefab +++ b/Assets/Prefabs/Weapons/PistolProjectile.prefab @@ -107,7 +107,7 @@ BoxCollider2D: m_CallbackLayers: serializedVersion: 2 m_Bits: 4294967295 - m_IsTrigger: 0 + m_IsTrigger: 1 m_UsedByEffector: 0 m_CompositeOperation: 0 m_CompositeOrder: 0 diff --git a/Assets/Scenes/main.unity b/Assets/Scenes/main.unity index 33a2082..7dd27ed 100644 --- a/Assets/Scenes/main.unity +++ b/Assets/Scenes/main.unity @@ -6313,6 +6313,7 @@ GameObject: - component: {fileID: 1261447611} - component: {fileID: 1261447610} - component: {fileID: 1261447609} + - component: {fileID: 1261447613} m_Layer: 6 m_Name: Player m_TagString: Player @@ -6418,7 +6419,7 @@ MonoBehaviour: level: 1 experience: 0 baseExperienceToLevelUp: 100 - damage: 1 + damage: 49 rangedDamage: 1 meleeDamage: 1 attackRange: 16 @@ -6479,6 +6480,7 @@ MonoBehaviour: character: {fileID: 1261447603} deathBehavior: _implementer: {fileID: 1261447606} + health: {fileID: 1261447608} --- !u!114 &1261447608 MonoBehaviour: m_ObjectHideFlags: 0 @@ -6550,6 +6552,21 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: character: {fileID: 1261447603} +--- !u!114 &1261447613 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1261447599} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f973da80e1dd49c8a9efdc270992b705, type: 3} + m_Name: + m_EditorClassIdentifier: + mainCamera: {fileID: 519420031} + weapons: [] + weaponsManager: {fileID: 1261447612} --- !u!1 &1300312975 GameObject: m_ObjectHideFlags: 0 @@ -6951,7 +6968,7 @@ PrefabInstance: objectReference: {fileID: 1261447609} - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} propertyPath: possibleItems.Array.size - value: 1 + value: 2 objectReference: {fileID: 0} - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} propertyPath: possibleWeapons.Array.size @@ -6961,6 +6978,10 @@ PrefabInstance: propertyPath: 'possibleItems.Array.data[0]' value: objectReference: {fileID: 11400000, guid: 08c8a414828d19951b3a413f631a6d70, type: 2} + - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} + propertyPath: 'possibleItems.Array.data[1]' + value: + objectReference: {fileID: 11400000, guid: 5366536d04e61959f911fa119df9eca4, type: 2} - target: {fileID: 1501730282976965803, guid: 4d1c30f952fa59fb9b0cc1a987f40ad8, type: 3} propertyPath: 'possibleWeapons.Array.data[0]' value: diff --git a/Assets/Scripts/Interfaces/IDeathBehavior.cs b/Assets/Scripts/Interfaces/IDeathBehavior.cs index 1d42e7e..a891a65 100644 --- a/Assets/Scripts/Interfaces/IDeathBehavior.cs +++ b/Assets/Scripts/Interfaces/IDeathBehavior.cs @@ -1,7 +1,10 @@ +using JetBrains.Annotations; +using UnityEngine; + namespace Interfaces { public interface IDeathBehavior { - void Die(); + void Die([CanBeNull] GameObject killer = null); } } \ No newline at end of file diff --git a/Assets/Scripts/Interfaces/IOnKillEffect.cs b/Assets/Scripts/Interfaces/IOnKillEffect.cs new file mode 100644 index 0000000..1fa823d --- /dev/null +++ b/Assets/Scripts/Interfaces/IOnKillEffect.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace Interfaces +{ + public interface IOnKillEffect + { + void OnKill(GameObject killer, GameObject victim); + } +} \ No newline at end of file diff --git a/Assets/Scripts/Interfaces/IOnKillEffect.cs.meta b/Assets/Scripts/Interfaces/IOnKillEffect.cs.meta new file mode 100644 index 0000000..25dcdba --- /dev/null +++ b/Assets/Scripts/Interfaces/IOnKillEffect.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3237e2bd7db84674bd34467854e53928 +timeCreated: 1752314256 \ No newline at end of file diff --git a/Assets/Scripts/Modifiers/HealOnKillModifier.cs b/Assets/Scripts/Modifiers/HealOnKillModifier.cs new file mode 100644 index 0000000..39d5c2f --- /dev/null +++ b/Assets/Scripts/Modifiers/HealOnKillModifier.cs @@ -0,0 +1,25 @@ +using System; +using Data; +using Interfaces; +using Systems; +using UnityEngine; + +namespace Modifiers +{ + [Serializable] + public class HealOnKillModifier : IStatModifier, IOnKillEffect + { + public float value; + public string Description => $"+{value} Health on Kill"; + + public void Apply(CharacterAttributes attributes) { } + + public void Remove(CharacterAttributes attributes) { } + + public void OnKill(GameObject killer, GameObject victim) + { + killer.TryGetComponent(out Character character); + character?.attributes.ModifyHealth(value); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Modifiers/HealOnKillModifier.cs.meta b/Assets/Scripts/Modifiers/HealOnKillModifier.cs.meta new file mode 100644 index 0000000..5485439 --- /dev/null +++ b/Assets/Scripts/Modifiers/HealOnKillModifier.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b329a84895234a3290156c5afe257212 +timeCreated: 1752314314 \ No newline at end of file diff --git a/Assets/Scripts/Systems/CharacterModifierManager.cs b/Assets/Scripts/Systems/CharacterModifierManager.cs index da95218..02953dc 100644 --- a/Assets/Scripts/Systems/CharacterModifierManager.cs +++ b/Assets/Scripts/Systems/CharacterModifierManager.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Data; using Interfaces; @@ -10,20 +11,50 @@ namespace Systems public class CharacterModifierManager : MonoBehaviour { [OdinSerialize] private List activeModifiers = new(); + [OdinSerialize] private List onKillEffects = new(); [SerializeField, Self] private Character character; - + + private void OnEnable() + { + EnemyDeathBehavior.OnAnyEnemyKilled += HandleEnemyKilled; + } + + private void OnDisable() + { + EnemyDeathBehavior.OnAnyEnemyKilled -= HandleEnemyKilled; + } + public void EquipItem(IStatModifier modifier) { activeModifiers.Add(modifier); modifier.Apply(character.attributes); + + if (modifier is IOnKillEffect onKillEffect) + { + onKillEffects.Add(onKillEffect); + } } public void UnequipItem(IStatModifier modifier) { - if (activeModifiers.Remove(modifier)) + if (!activeModifiers.Remove(modifier)) return; + + modifier.Remove(character.attributes); + + if (modifier is IOnKillEffect onKillEffect) { - modifier.Remove(character.attributes); + onKillEffects.Remove(onKillEffect); + } + } + + private void HandleEnemyKilled(GameObject killer, GameObject victim) + { + if (killer != gameObject) return; + + foreach (var effect in onKillEffects) + { + effect.OnKill(killer, victim); } } } diff --git a/Assets/Scripts/Systems/CharacterWeaponsManager.cs b/Assets/Scripts/Systems/CharacterWeaponsManager.cs index 32d38ba..392f991 100644 --- a/Assets/Scripts/Systems/CharacterWeaponsManager.cs +++ b/Assets/Scripts/Systems/CharacterWeaponsManager.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using Data; +using Inventory; using KBCore.Refs; using Sirenix.Serialization; using UnityEngine; @@ -13,6 +15,9 @@ namespace Systems [SerializeField] private IReadOnlyList EquippedWeapons => equippedWeapons.AsReadOnly(); [SerializeField, Self] private Character character; + + public event Action WeaponEquipped; + public event Action WeaponUnequipped; public Weapon EquipWeapon(GameObject weaponPrefab) { @@ -22,6 +27,7 @@ namespace Systems weapon.character = character; equippedWeapons.Add(weapon); weapon.enabled = true; + WeaponEquipped?.Invoke(weapon); return weapon; } @@ -33,6 +39,7 @@ namespace Systems { if (!equippedWeapons.Remove(weapon)) return; weapon.enabled = false; + WeaponUnequipped?.Invoke(weapon); if (!destroy) return; Destroy(weapon.gameObject); @@ -43,6 +50,7 @@ namespace Systems foreach (var weapon in equippedWeapons) { weapon.enabled = false; + WeaponUnequipped?.Invoke(weapon); } } @@ -51,6 +59,7 @@ namespace Systems foreach (var weapon in equippedWeapons) { weapon.enabled = true; + WeaponEquipped?.Invoke(weapon); } } @@ -58,6 +67,7 @@ namespace Systems { foreach (var weapon in equippedWeapons) { + WeaponUnequipped?.Invoke(weapon); Destroy(weapon.gameObject); } equippedWeapons.Clear(); diff --git a/Assets/Scripts/Systems/DeathHandler.cs b/Assets/Scripts/Systems/DeathHandler.cs index 6039a35..4abfb8e 100644 --- a/Assets/Scripts/Systems/DeathHandler.cs +++ b/Assets/Scripts/Systems/DeathHandler.cs @@ -9,6 +9,7 @@ namespace Systems { [Self, SerializeField] private Character character; [Self, SerializeField] private InterfaceRef deathBehavior; + [Self, SerializeField] private Health health; private void OnEnable() { @@ -30,7 +31,8 @@ namespace Systems private void Die() { - deathBehavior.Value.Die(); + var lastAttacker = health.LastAttacker; + deathBehavior.Value.Die(lastAttacker); } } } \ No newline at end of file diff --git a/Assets/Scripts/Systems/EnemyDeathBehavior.cs b/Assets/Scripts/Systems/EnemyDeathBehavior.cs index b230227..409764c 100644 --- a/Assets/Scripts/Systems/EnemyDeathBehavior.cs +++ b/Assets/Scripts/Systems/EnemyDeathBehavior.cs @@ -1,3 +1,4 @@ +using System; using Interfaces; using Sirenix.Serialization; using UnityEngine; @@ -6,13 +7,18 @@ namespace Systems { public class EnemyDeathBehavior : MonoBehaviour, IDeathBehavior { + public static event Action OnAnyEnemyKilled; + [OdinSerialize, SerializeField] private int expReward = 5; [OdinSerialize, SerializeField] private int coinReward = 1; - public void Die() + public void Die(GameObject killer = null) { GameManager.Instance.Player.attributes.ModifyExperience(expReward); GameManager.Instance.AddCoins(coinReward); + + OnAnyEnemyKilled?.Invoke(killer ?? GameManager.Instance.Player.gameObject, gameObject); + Destroy(gameObject); // later let's add particle effects, sound effects, etc. diff --git a/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs b/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs new file mode 100644 index 0000000..a1e0735 --- /dev/null +++ b/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using UnityEngine; +using Weapons; + +namespace Systems +{ + public class EnemyWeaponTargetSetter : MonoBehaviour + { + [SerializeField] private List weapons = new(); + [SerializeField] private Transform target; + + private void Reset() + { + if (weapons.Count == 0) + { + weapons = new List(GetComponentsInChildren()); + } + } + + private void Update() + { + if (!target || weapons.Count == 0) return; + + foreach (var weapon in weapons) + { + weapon.Target = target.position; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs.meta b/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs.meta new file mode 100644 index 0000000..1a5ac68 --- /dev/null +++ b/Assets/Scripts/Systems/EnemyWeaponTargetSetter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5523bce9a0d94d6fafb3fc09e3bf9cc6 +timeCreated: 1752316561 \ No newline at end of file diff --git a/Assets/Scripts/Systems/Health.cs b/Assets/Scripts/Systems/Health.cs index 3147d2d..42ce115 100644 --- a/Assets/Scripts/Systems/Health.cs +++ b/Assets/Scripts/Systems/Health.cs @@ -7,16 +7,21 @@ namespace Systems { public class Health : MonoBehaviour { + private GameObject lastAttacker; + [Self, SerializeField] private Character character; [SerializeField] private float initialHealth = 100f; + + public GameObject LastAttacker => lastAttacker; private void Start() { character.attributes.SetHealth(initialHealth); } - public void TakeDamage(float damage) + public void TakeDamage(float damage, GameObject attacker = null) { + lastAttacker = attacker; var effectiveDamage = Math.Max(damage - character.attributes.Armor, 1); character.attributes.ModifyHealth(-effectiveDamage); } diff --git a/Assets/Scripts/Systems/InventoryManager.cs b/Assets/Scripts/Systems/InventoryManager.cs index 0d1c36b..f376006 100644 --- a/Assets/Scripts/Systems/InventoryManager.cs +++ b/Assets/Scripts/Systems/InventoryManager.cs @@ -12,6 +12,9 @@ namespace Systems [SerializeField, Self] private Inventory.Inventory inventory; [SerializeField, Self] private CharacterModifierManager characterModifierManager; [SerializeField, Self] private CharacterWeaponsManager characterWeaponsManager; + + public event Action ItemEquipped; + public event Action ItemUnequipped; private void Start() { @@ -42,6 +45,7 @@ namespace Systems inventory.AddItem(item); foreach (var cure in item.cures) characterModifierManager.EquipItem(cure); foreach (var curse in item.curses) characterModifierManager.EquipItem(curse); + ItemEquipped?.Invoke(item); } public void UnequipItem(StatModifierItem item) @@ -51,6 +55,7 @@ namespace Systems foreach (var cure in item.cures) characterModifierManager.UnequipItem(cure); foreach (var curse in item.curses) characterModifierManager.UnequipItem(curse); + ItemUnequipped?.Invoke(item); } public void EquipWeapon(WeaponItem weaponItem) diff --git a/Assets/Scripts/Systems/PlayerDeathBehavior.cs b/Assets/Scripts/Systems/PlayerDeathBehavior.cs index 5d871e1..33feb68 100644 --- a/Assets/Scripts/Systems/PlayerDeathBehavior.cs +++ b/Assets/Scripts/Systems/PlayerDeathBehavior.cs @@ -6,7 +6,7 @@ namespace Systems { public class PlayerDeathBehavior : MonoBehaviour, IDeathBehavior { - public void Die() + public void Die(GameObject killer = null) { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } diff --git a/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs b/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs new file mode 100644 index 0000000..538b0ea --- /dev/null +++ b/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using Inventory; +using KBCore.Refs; +using UnityEngine; +using UnityEngine.InputSystem; +using Weapons; + +namespace Systems +{ + public class PlayerWeaponTargetSetter : MonoBehaviour + { + [SerializeField, Scene] private Camera mainCamera; + [SerializeField] private List weapons = new(); + [Self, SerializeField] private CharacterWeaponsManager weaponsManager; + + private void OnEnable() + { + weaponsManager.WeaponEquipped += OnWeaponEquipped; + weaponsManager.WeaponUnequipped += OnWeaponUnequipped; + } + + private void OnDisable() + { + weaponsManager.WeaponEquipped -= OnWeaponEquipped; + weaponsManager.WeaponUnequipped -= OnWeaponUnequipped; + } + + private void Reset() + { + if (!mainCamera) mainCamera = Camera.main; + if (weapons == null || weapons.Count == 0) + { + weapons = new List(GetComponentsInChildren()); + } + } + + private void Update() + { + if (!mainCamera || weapons.Count == 0) return; + + var mouseScreen = Mouse.current.position.ReadValue(); + var mouseWorld = mainCamera.ScreenToWorldPoint(mouseScreen); + mouseWorld.z = 0f; + + foreach (var weapon in weapons) + { + weapon.Target = mouseWorld; + } + } + + private void OnWeaponEquipped(Weapon weapon) + { + if (!weapon || weapon is not AutoWeapon autoWeapon) return; + + if (autoWeapon && !weapons.Contains(autoWeapon)) + { + weapons.Add(autoWeapon); + } + } + + private void OnWeaponUnequipped(Weapon weapon) + { + if (!weapon || weapon is not AutoWeapon autoWeapon) return; + + if (autoWeapon && weapons.Contains(autoWeapon)) + { + weapons.Remove(autoWeapon); + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs.meta b/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs.meta new file mode 100644 index 0000000..6e7a374 --- /dev/null +++ b/Assets/Scripts/Systems/PlayerWeaponTargetSetter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f973da80e1dd49c8a9efdc270992b705 +timeCreated: 1752316388 \ No newline at end of file diff --git a/Assets/Scripts/Weapons/AutoWeapon.cs b/Assets/Scripts/Weapons/AutoWeapon.cs index d9d6aa3..5ad8be4 100644 --- a/Assets/Scripts/Weapons/AutoWeapon.cs +++ b/Assets/Scripts/Weapons/AutoWeapon.cs @@ -8,8 +8,14 @@ namespace Weapons [SerializeField] private GameObject projectilePrefab; [SerializeField] private Transform firePoint; + public Vector2 Target { get; set; } + public override void Fire() { + var direction = (Target - (Vector2)firePoint.position).normalized; + firePoint.up = direction; + Debug.DrawLine(firePoint.position, Target, Color.red, 2f); + var projectile = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation); projectile.TryGetComponent(out var inflector);