stats, items wip

This commit is contained in:
Pedro Gomes 2025-09-23 16:53:21 +01:00
parent 111473b38a
commit 2ebb21cf9d
19 changed files with 848 additions and 183 deletions

View File

@ -4,122 +4,227 @@ using System.Collections.ObjectModel;
using System.Linq;
using UnityEngine.Events;
public enum CharacterStatType
{
Cunning,
Flow,
Presence,
AttackDamage,
SpellDamage,
AttackSpeed,
CritChance,
CritDamage,
AuraPower,
MaxHealth,
HealthRegen,
MaxMana,
ManaRegen,
Armor,
MagicResistance,
DodgeChance,
BlockChance,
BlockEffectiveness,
AreaEffectiveness,
CooldownReduction,
MovementSpeed,
ReputationGainIncrease,
GoldCostReduction
}
public static class CharacterStatHelper
{
public static List<CharacterStatType> Attributes = new List<CharacterStatType>()
{
CharacterStatType.Cunning,
CharacterStatType.Flow,
CharacterStatType.Presence
};
public static bool IsAttribute(CharacterStatType type)
{
return Attributes.Contains(type);
}
public static List<CharacterStatType> Offensive = new List<CharacterStatType>()
{
CharacterStatType.AttackDamage,
CharacterStatType.SpellDamage,
CharacterStatType.AttackSpeed,
CharacterStatType.CritChance,
CharacterStatType.CritDamage,
CharacterStatType.AuraPower,
};
public static bool IsOffensive(CharacterStatType type)
{
return Offensive.Contains(type);
}
public static List<CharacterStatType> Defensive = new List<CharacterStatType>()
{
CharacterStatType.Armor,
CharacterStatType.MagicResistance,
CharacterStatType.DodgeChance,
CharacterStatType.BlockChance,
CharacterStatType.BlockEffectiveness,
};
public static bool IsDefensive(CharacterStatType type)
{
return Defensive.Contains(type);
}
public static List<CharacterStatType> Resource = new List<CharacterStatType>()
{
CharacterStatType.MaxHealth,
CharacterStatType.HealthRegen,
CharacterStatType.MaxMana,
CharacterStatType.ManaRegen,
};
public static bool IsResource(CharacterStatType type)
{
return Resource.Contains(type);
}
public static List<CharacterStatType> Control = new List<CharacterStatType>()
{
CharacterStatType.AreaEffectiveness,
CharacterStatType.CooldownReduction,
CharacterStatType.MovementSpeed,
};
public static bool IsControl(CharacterStatType type)
{
return Control.Contains(type);
}
public static List<CharacterStatType> Misc = new List<CharacterStatType>()
{
CharacterStatType.ReputationGainIncrease,
CharacterStatType.GoldCostReduction,
};
public static bool IsMisc(CharacterStatType type)
{
return Misc.Contains(type);
}
}
namespace Kryz.CharacterStats
{
[Serializable]
public class CharacterStat
{
public GameTag statTag;
[Serializable]
public class CharacterStat
{
public GameTag statTag;
public CharacterStatType statType;
public float BaseValue;
public float BaseValue;
protected bool isDirty = true;
protected float lastBaseValue;
protected bool isDirty = true;
protected float lastBaseValue;
protected float _value;
public virtual float Value {
get {
if(isDirty || lastBaseValue != BaseValue) {
lastBaseValue = BaseValue;
_value = CalculateFinalValue();
isDirty = false;
}
return _value;
}
}
protected readonly List<StatModifier> statModifiers;
public readonly ReadOnlyCollection<StatModifier> StatModifiers;
public CharacterStat()
{
statModifiers = new List<StatModifier>();
StatModifiers = statModifiers.AsReadOnly();
}
public CharacterStat(float baseValue) : this()
{
isDirty = true;
BaseValue = baseValue;
}
public virtual void AddModifier(StatModifier mod)
{
isDirty = true;
statModifiers.Add(mod);
}
public virtual bool RemoveModifier(StatModifier mod)
{
if (statModifiers.Remove(mod))
{
isDirty = true;
return true;
}
return false;
}
public virtual bool HasModifiersFromSource(object source)
protected float _value;
public virtual float Value
{
return StatModifiers.Any(mod => mod.Source == source);
}
get
{
if (isDirty || lastBaseValue != BaseValue)
{
lastBaseValue = BaseValue;
_value = CalculateFinalValue();
isDirty = false;
}
return _value;
}
}
public virtual bool RemoveAllModifiersFromSource(object source)
{
int numRemovals = statModifiers.RemoveAll(mod => mod.Source == source);
protected readonly List<StatModifier> statModifiers;
public readonly ReadOnlyCollection<StatModifier> StatModifiers;
if (numRemovals > 0)
{
isDirty = true;
return true;
}
return false;
}
public CharacterStat()
{
statModifiers = new List<StatModifier>();
StatModifiers = statModifiers.AsReadOnly();
}
protected virtual int CompareModifierOrder(StatModifier a, StatModifier b)
{
if (a.Order < b.Order)
return -1;
else if (a.Order > b.Order)
return 1;
return 0; //if (a.Order == b.Order)
}
protected virtual float CalculateFinalValue()
{
float finalValue = BaseValue;
float sumPercentAdd = 0;
public CharacterStat(float baseValue) : this()
{
isDirty = true;
BaseValue = baseValue;
}
statModifiers.Sort(CompareModifierOrder);
public virtual void AddModifier(StatModifier mod)
{
isDirty = true;
statModifiers.Add(mod);
}
for (int i = 0; i < statModifiers.Count; i++)
{
StatModifier mod = statModifiers[i];
public virtual bool RemoveModifier(StatModifier mod)
{
if (statModifiers.Remove(mod))
{
isDirty = true;
return true;
}
return false;
}
if (mod.Type == StatModType.Flat)
{
finalValue += mod.Value;
}
else if (mod.Type == StatModType.PercentAdd)
{
sumPercentAdd += mod.Value;
public virtual bool HasModifiersFromSource(object source)
{
return StatModifiers.Any(mod => mod.Source == source);
}
if (i + 1 >= statModifiers.Count || statModifiers[i + 1].Type != StatModType.PercentAdd)
{
finalValue *= 1 + sumPercentAdd;
sumPercentAdd = 0;
}
}
else if (mod.Type == StatModType.PercentMult)
{
finalValue *= 1 + mod.Value;
}
}
public virtual bool RemoveAllModifiersFromSource(object source)
{
int numRemovals = statModifiers.RemoveAll(mod => mod.Source == source);
// Workaround for float calculation errors, like displaying 12.00001 instead of 12
return (float)Math.Round(finalValue, 4);
}
}
if (numRemovals > 0)
{
isDirty = true;
return true;
}
return false;
}
protected virtual int CompareModifierOrder(StatModifier a, StatModifier b)
{
if (a.Order < b.Order)
return -1;
else if (a.Order > b.Order)
return 1;
return 0; //if (a.Order == b.Order)
}
protected virtual float CalculateFinalValue()
{
float finalValue = BaseValue;
float sumPercentAdd = 0;
statModifiers.Sort(CompareModifierOrder);
for (int i = 0; i < statModifiers.Count; i++)
{
StatModifier mod = statModifiers[i];
if (mod.Type == StatModType.Flat)
{
finalValue += mod.Value;
}
else if (mod.Type == StatModType.PercentAdd)
{
sumPercentAdd += mod.Value;
if (i + 1 >= statModifiers.Count || statModifiers[i + 1].Type != StatModType.PercentAdd)
{
finalValue *= 1 + sumPercentAdd;
sumPercentAdd = 0;
}
}
else if (mod.Type == StatModType.PercentMult)
{
finalValue *= 1 + mod.Value;
}
}
// Workaround for float calculation errors, like displaying 12.00001 instead of 12
return (float)Math.Round(finalValue, 4);
}
}
}

View File

@ -8027,7 +8027,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2440268520758508678, guid: 1372ea2f917805749a87f6c81e9938cf, type: 3}
propertyPath: m_Name
value: OffWeapon Slot
value: OffHand Slot
objectReference: {fileID: 0}
- target: {fileID: 2440268520765564492, guid: 1372ea2f917805749a87f6c81e9938cf, type: 3}
propertyPath: m_Sprite
@ -8149,7 +8149,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2440268520758508678, guid: 1372ea2f917805749a87f6c81e9938cf, type: 3}
propertyPath: m_Name
value: MainWeapon Slot
value: MainHand Slot
objectReference: {fileID: 0}
- target: {fileID: 2440268520765564492, guid: 1372ea2f917805749a87f6c81e9938cf, type: 3}
propertyPath: m_Sprite

View File

@ -71,6 +71,36 @@ namespace Kryz.CharacterStats.Examples
protected virtual void Awake()
{
Cunning.statType = CharacterStatType.Cunning;
Flow.statType = CharacterStatType.Flow;
Presence.statType = CharacterStatType.Presence;
AttackDamage.statType = CharacterStatType.AttackDamage;
SpellDamage.statType = CharacterStatType.SpellDamage;
AttackSpeed.statType = CharacterStatType.AttackSpeed;
CritChance.statType = CharacterStatType.CritChance;
CritDamage.statType = CharacterStatType.CritDamage;
AuraPower.statType = CharacterStatType.AuraPower;
MaxHealth.statType = CharacterStatType.MaxHealth;
HealthRegen.statType = CharacterStatType.HealthRegen;
MaxMana.statType = CharacterStatType.MaxMana;
ManaRegen.statType = CharacterStatType.ManaRegen;
Armor.statType = CharacterStatType.Armor;
MagicResistance.statType = CharacterStatType.MagicResistance;
DodgeChance.statType = CharacterStatType.DodgeChance;
BlockChance.statType = CharacterStatType.BlockChance;
BlockEffectiveness.statType = CharacterStatType.BlockEffectiveness;
AreaEffectiveness.statType = CharacterStatType.AreaEffectiveness;
CooldownReduction.statType = CharacterStatType.CooldownReduction;
MovementSpeed.statType = CharacterStatType.MovementSpeed;
ReputationGainIncrease.statType = CharacterStatType.ReputationGainIncrease;
GoldCostReduction.statType = CharacterStatType.GoldCostReduction;
primaryStatsDictionary.Add(nameof(Cunning).ToLower(), Cunning);
primaryStatsDictionary.Add(nameof(Flow).ToLower(), Flow);
primaryStatsDictionary.Add(nameof(Presence).ToLower(), Presence);

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Kryz.CharacterStats.Examples
@ -55,7 +56,7 @@ namespace Kryz.CharacterStats.Examples
{
for (int i = 0; i < equipmentSlots.Length; i++)
{
if(equipmentSlots[i].EquipmentType == slot)
if (equipmentSlots[i].EquipmentType == slot)
{
return (EquippableItemInstance)equipmentSlots[i].Item;
}
@ -63,22 +64,185 @@ namespace Kryz.CharacterStats.Examples
return null;
}
public bool AddItem(EquippableItemInstance item, out EquippableItemInstance previousItem)
// NEW: Helper method to get weapon slots
public (EquipmentSlot mainHand, EquipmentSlot offHand) GetWeaponSlots()
{
EquipmentSlot mainHand = null;
EquipmentSlot offHand = null;
for (int i = 0; i < equipmentSlots.Length; i++)
{
if (equipmentSlots[i].EquipmentType == EquipmentType.MainHand)
mainHand = equipmentSlots[i];
else if (equipmentSlots[i].EquipmentType == EquipmentType.OffHand)
offHand = equipmentSlots[i];
}
return (mainHand, offHand);
}
/// <summary>
/// Calculates how many items would be displaced if the given item were equipped
/// </summary>
public int GetDisplacedItemCount(EquippableItemInstance item)
{
if (!item.IsWeapon)
{
// Non-weapons: at most 1 item displaced (standard replacement)
var existingItem = GetEquippedItemOnSpecificSlot(item.EquipmentType);
return (existingItem != null && !string.IsNullOrEmpty(existingItem.ItemName)) ? 1 : 0;
}
// Handle weapons
var (mainHandSlot, offHandSlot) = GetWeaponSlots();
if (mainHandSlot == null || offHandSlot == null)
return 0;
var currentMainHand = (EquippableItemInstance)mainHandSlot.Item;
var currentOffHand = (EquippableItemInstance)offHandSlot.Item;
if (item.IsTwoHandedWeapon)
{
// Two-handed weapon displaces both current weapons
int count = 0;
if (currentMainHand != null && !string.IsNullOrEmpty(currentMainHand.ItemName))
count++;
if (currentOffHand != null && !string.IsNullOrEmpty(currentOffHand.ItemName))
count++;
return count;
}
else // One-handed weapon
{
if (currentMainHand != null && currentMainHand.IsTwoHandedWeapon)
{
// Replacing two-handed with one-handed
return 1;
}
else
{
// Normal one-handed logic
if (currentMainHand == null || currentOffHand == null)
return 0; // Filling empty slot
else
return 1; // Will replace main hand
}
}
}
// NEW: Helper method to get slot index for saving
private int GetSlotIndex(EquipmentSlot slot)
{
for (int i = 0; i < equipmentSlots.Length; i++)
{
if (equipmentSlots[i].EquipmentType == item.EquipmentType)
{
previousItem = (EquippableItemInstance)equipmentSlots[i].Item;
equipmentSlots[i].Item = item;
if (equipmentSlots[i] == slot)
return i;
}
return -1;
}
// UPDATED: Modified to handle two-handed weapon logic
public bool AddItem(EquippableItemInstance item, out EquippableItemInstance previousItem)
{
return AddItem(item, out previousItem, out _);
}
// NEW: Overloaded method that returns multiple previous items for two-handed weapon swapping
public bool AddItem(EquippableItemInstance item, out EquippableItemInstance previousItem, out EquippableItemInstance secondPreviousItem)
{
previousItem = null;
secondPreviousItem = null;
// Handle non-weapon items with original logic
if (!item.IsWeapon)
{
for (int i = 0; i < equipmentSlots.Length; i++)
{
if (equipmentSlots[i].EquipmentType == item.EquipmentType)
{
previousItem = (EquippableItemInstance)equipmentSlots[i].Item;
equipmentSlots[i].Item = item;
equipmentData.equippedItems[i] = item;
PlayerDataHandler.Instance.SaveCharacterEquipmentData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, equipmentData);
return true;
}
}
return false;
}
// Handle weapon items
var (mainHandSlot, offHandSlot) = GetWeaponSlots();
if (mainHandSlot == null || offHandSlot == null)
return false;
var currentMainHand = (EquippableItemInstance)mainHandSlot.Item;
var currentOffHand = (EquippableItemInstance)offHandSlot.Item;
if (item.IsTwoHandedWeapon)
{
// Equipping a two-handed weapon
previousItem = currentMainHand;
secondPreviousItem = currentOffHand;
// Set the two-handed weapon in main hand, clear off-hand
mainHandSlot.Item = item;
offHandSlot.Item = null;
// Update data arrays
int mainHandIndex = GetSlotIndex(mainHandSlot);
int offHandIndex = GetSlotIndex(offHandSlot);
equipmentData.equippedItems[mainHandIndex] = item;
equipmentData.equippedItems[offHandIndex] = null;
PlayerDataHandler.Instance.SaveCharacterEquipmentData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, equipmentData);
return true;
}
else // One-handed weapon
{
// Check if currently have a two-handed weapon equipped
if (currentMainHand != null && currentMainHand.IsTwoHandedWeapon)
{
// Replace two-handed with one-handed in main hand
previousItem = currentMainHand;
mainHandSlot.Item = item;
int mainHandIndex = GetSlotIndex(mainHandSlot);
equipmentData.equippedItems[mainHandIndex] = item;
PlayerDataHandler.Instance.SaveCharacterEquipmentData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, equipmentData);
return true;
}
else
{
// Normal one-handed weapon equipping logic
// Prioritize main hand, then off-hand if main hand is occupied
if (currentMainHand == null)
{
// Equip in main hand
mainHandSlot.Item = item;
int mainHandIndex = GetSlotIndex(mainHandSlot);
equipmentData.equippedItems[mainHandIndex] = item;
}
else if (currentOffHand == null)
{
// Equip in off-hand for dual wielding
offHandSlot.Item = item;
int offHandIndex = GetSlotIndex(offHandSlot);
equipmentData.equippedItems[offHandIndex] = item;
}
else
{
// Both slots occupied, replace main hand
previousItem = currentMainHand;
mainHandSlot.Item = item;
int mainHandIndex = GetSlotIndex(mainHandSlot);
equipmentData.equippedItems[mainHandIndex] = item;
}
equipmentData.equippedItems[i] = item;
PlayerDataHandler.Instance.SaveCharacterEquipmentData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, equipmentData);
return true;
}
}
previousItem = null;
return false;
}
public bool RemoveItem(EquippableItemInstance item)
@ -135,4 +299,4 @@ namespace Kryz.CharacterStats.Examples
}
}
}
}

View File

@ -1,4 +1,55 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
public enum ItemRarity
{
Common,
Rare,
Epic,
Unique,
Legendary
}
public enum WeaponType
{
// Two-Handed Weapons (Build-defining)
Staff,
Spear,
Scythe,
Hammer,
Bow,
Crossbow,
Axe,
// One-Handed Weapons (Flexible)
Sword,
Shield,
Dagger,
Book
}
public static class WeaponTypeExtensions
{
private static readonly HashSet<WeaponType> TwoHandedWeapons = new()
{
WeaponType.Staff,
WeaponType.Spear,
WeaponType.Scythe,
WeaponType.Hammer,
WeaponType.Bow,
WeaponType.Crossbow,
WeaponType.Axe
};
public static bool IsTwoHanded(this WeaponType weaponType)
{
return TwoHandedWeapons.Contains(weaponType);
}
public static bool IsOneHanded(this WeaponType weaponType)
{
return !IsTwoHanded(weaponType);
}
}
namespace Kryz.CharacterStats.Examples
{
@ -13,13 +64,14 @@ namespace Kryz.CharacterStats.Examples
Gloves,
Boots,
MainWeapon,
OffWeapon,
MainHand,
OffHand,
Accessory,
Amulet,
//Accessory,
//Amulet,
}
[CreateAssetMenu]
public class EquippableItem : Item
{
@ -77,6 +129,19 @@ namespace Kryz.CharacterStats.Examples
public EquipmentType EquipmentType;
[Space]
// NEW: Add WeaponType for weapon items
public WeaponType WeaponType;
// NEW: Helper property to check if this item is a weapon
public bool IsWeapon => EquipmentType == EquipmentType.MainHand || EquipmentType == EquipmentType.OffHand;
// NEW: Helper property to check if this weapon is two-handed
public bool IsTwoHandedWeapon => IsWeapon && WeaponType.IsTwoHanded();
// NEW: Helper property to check if this weapon is one-handed
public bool IsOneHandedWeapon => IsWeapon && WeaponType.IsOneHanded();
[Space]
public bool CraftableBase = false;
[Space(20f)]

View File

@ -62,6 +62,9 @@ public class EquippableItemInstance : ItemInstance
[Space]
public EquipmentType EquipmentType;
[Space]
// NEW: Add WeaponType for weapon items
public WeaponType WeaponType;
[Space]
public bool CraftableBase = false;
[Space]
/// <summary>
@ -72,6 +75,15 @@ public class EquippableItemInstance : ItemInstance
public int MaxTotalUniqueStatsIncreasedByStones;
public List<string> AddedStoneStats = new List<string>();
// NEW: Helper property to check if this item is a weapon
public bool IsWeapon => EquipmentType == EquipmentType.MainHand || EquipmentType == EquipmentType.OffHand;
// NEW: Helper property to check if this weapon is two-handed
public bool IsTwoHandedWeapon => IsWeapon && WeaponType.IsTwoHanded();
// NEW: Helper property to check if this weapon is one-handed
public bool IsOneHandedWeapon => IsWeapon && WeaponType.IsOneHanded();
public void Equip(PlayerCharacterStats c)
{
if (AttackDamageBonus != 0)
@ -276,6 +288,7 @@ public class EquippableItemInstance : ItemInstance
MagicResistancePercentBonus = 0;
EquipmentType = EquipmentType.Helmet;
WeaponType = WeaponType.Sword; // Default weapon type
CraftableBase = false;
@ -307,6 +320,8 @@ public class EquippableItemInstance : ItemInstance
MagicResistancePercentBonus = template.MagicResistancePercentBonus;
EquipmentType = template.EquipmentType;
// NEW: Copy WeaponType from template (assuming EquippableItem has this field)
WeaponType = template.WeaponType;
CraftableBase = template.CraftableBase;
MaxTotalUniqueStatsIncreasedByStones = template.MaxTotalUniqueStatsIncreasedByStones;
@ -364,9 +379,10 @@ public class EquippableItemInstance : ItemInstance
EquipmentType = template.EquipmentType;
// NEW: Copy WeaponType from template (assuming EquippableItem has this field)
WeaponType = template.WeaponType;
CraftableBase = template.CraftableBase;
MaxTotalUniqueStatsIncreasedByStones = template.MaxTotalUniqueStatsIncreasedByStones;
}
}
}

View File

@ -137,6 +137,10 @@ namespace Kryz.CharacterStats.Examples
return items.Count >= itemSlots.Length;
//return items.FindAll(x => string.IsNullOrEmpty(x.ItemName)).Count <= 0;
}
public int AvailableSlots()
{
return itemSlots.Length - items.Count;
}
public void LoadInventory()
{

View File

@ -204,20 +204,38 @@ namespace Kryz.CharacterStats.Examples
public void Equip(EquippableItemInstance item)
{
// First, check if we have enough inventory space for displaced items
if (!CanEquipItem(item))
{
// Could show a message to player here: "Not enough inventory space"
return;
}
if (inventory.RemoveItem(item))
{
ItemTooltip.Instance.HideTooltip();
EquippedItemTooltip.Instance.HideTooltip();
EquippableItemInstance previousItem;
if (equipmentPanel.AddItem(item, out previousItem))
EquippableItemInstance secondPreviousItem;
if (equipmentPanel.AddItem(item, out previousItem, out secondPreviousItem))
{
// Handle first previous item
if (previousItem != null && !string.IsNullOrEmpty(previousItem.ItemName))
{
inventory.AddItem(previousItem);
previousItem.Unequip(this);
statPanel.UpdateStatValues();
}
// Handle second previous item (for two-handed weapon replacement)
if (secondPreviousItem != null && !string.IsNullOrEmpty(secondPreviousItem.ItemName))
{
inventory.AddItem(secondPreviousItem);
secondPreviousItem.Unequip(this);
}
// Equip the new item
item.Equip(this);
statPanel.UpdateStatValues();
onUpdateStatValues.Invoke();
@ -229,6 +247,18 @@ namespace Kryz.CharacterStats.Examples
}
}
// Simplified version using the EquipmentPanel helper method
private bool CanEquipItem(EquippableItemInstance item)
{
int itemsToDisplace = equipmentPanel.GetDisplacedItemCount(item);
// Calculate available inventory slots
// We get +1 slot from removing the item we're equipping
int availableSlots = inventory.AvailableSlots() + 1;
return availableSlots >= itemsToDisplace;
}
public void Unequip(EquippableItemInstance item)
{
if (!inventory.IsFull() && equipmentPanel.RemoveItem(item))

View File

@ -3702,6 +3702,69 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &2097151069981005797
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7335229491322327302}
- component: {fileID: 4411192570680935153}
m_Layer: 0
m_Name: EquippableItemInstanceGenerator
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &7335229491322327302
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2097151069981005797}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7475116342638198534}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4411192570680935153
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2097151069981005797}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a85a7f34d000be84fa64ef31a99b20b4, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::EquippableItemGenerator
HelmetIcons: []
ShoulderIcons: []
ChestIcons: []
BeltIcons: []
LegsIcons: []
BracersIcons: []
GlovesIcons: []
BootsIcons: []
StaffIcons: []
SpearIcons: []
ScytheIcons: []
HammerIcons: []
BowIcons: []
CrossbowIcons: []
AxeIcons: []
SwordIcons: []
ShieldIcons: []
DaggerIcons: []
BookIcons: []
--- !u!1 &2105173639354992659
GameObject:
m_ObjectHideFlags: 0
@ -7803,6 +7866,7 @@ MonoBehaviour:
ReputationGainIncreasePercentBonus: 0
GoldCostReductionPercentBonus: 0
EquipmentType: 0
WeaponType: 7
CraftableBase: 0
MaxTotalUniqueStatsIncreasedByStones: 0
AddedStoneStats: []
@ -14453,6 +14517,7 @@ Transform:
- {fileID: 315999698282489082}
- {fileID: 2800905167933415666}
- {fileID: 1867733016440364756}
- {fileID: 7335229491322327302}
m_Father: {fileID: 7475116341965418816}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7475116342667879665

View File

@ -78,7 +78,7 @@ Material:
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Rotation: 8.570184
- _Rotation: 89.72914
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1

View File

@ -1004,8 +1004,8 @@ MonoBehaviour:
m_Calls: []
m_text: Skelly Mage
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 01ed77f6af1621a4cb0e3ee10d3b7147, type: 2}
m_sharedMaterial: {fileID: -7243662068360240803, guid: 01ed77f6af1621a4cb0e3ee10d3b7147, type: 2}
m_fontAsset: {fileID: 11400000, guid: 41f9368fce25cb44ba223ac55f4e69e1, type: 2}
m_sharedMaterial: {fileID: -1192090701822111264, guid: 41f9368fce25cb44ba223ac55f4e69e1, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []

View File

@ -17,52 +17,37 @@ MonoBehaviour:
sellPricePlayer: 100
sellPriceVendor: 2000
description:
StrengthBonus: 0
AgilityBonus: 0
IntelligenceBonus: 1
SpiritBonus: 1
VitalityBonus: 0
StrengthPercentBonus: 0
AgilityPercentBonus: 0
IntelligencePercentBonus: 0
SpiritPercentBonus: 0
VitalityPercentBonus: 0
AttackDamageBonus: 0
SpellDamageBonus: 1
CritChanceBonus: 0
CritDamageBonus: 0
MaxHealthBonus: 0
HealthRegenBonus: 0
MaxManaBonus: 0
ManaRegenBonus: 0
ArmorBonus: 0
MagicResistanceBonus: 0
AttackDamagePercentBonus: 0
SpellDamagePercentBonus: 0
AttackSpeedPercentBonus: 0
CritChancePercentBonus: 0
CritDamagePercentBonus: 0
MaxHealthPercentBonus: 0
HealthRegenPercentBonus: 0
MaxManaPercentBonus: 0
ManaRegenPercentBonus: 0
ArmorPercentBonus: 0
MagicResistancePercentBonus: 0
EquipmentType: 5
DodgeChancePercentBonus: 0
BlockChancePercentBonus: 0
BlockEffectivenessPercentBonus: 0
AreaEffectivenessPercentBonus: 0
CooldownReductionPercentBonus: 0
MovementSpeedPercentBonus: 0
ReputationGainIncreasePercentBonus: 0
GoldCostReductionPercentBonus: 0
EquipmentType: 9
CraftableBase: 0
MinStrengthBonus: 0
MaxStrengthBonus: 0
MinAgilityBonus: 0
MaxAgilityBonus: 0
MinIntelligenceBonus: 0
MaxIntelligenceBonus: 0
MinSpiritBonus: 0
MaxSpiritBonus: 0
MinVitalityBonus: 0
MaxVitalityBonus: 0
MinStrengthPercentBonus: 0
MaxStrengthPercentBonus: 0
MinAgilityPercentBonus: 0
MaxAgilityPercentBonus: 0
MinIntelligencePercentBonus: 0
MaxIntelligencePercentBonus: 0
MinSpiritPercentBonus: 0
MaxSpiritPercentBonus: 0
MinVitalityPercentBonus: 0
MaxVitalityPercentBonus: 0
MinAttackDamageBonus: 0
MaxAttackDamageBonus: 0
MinSpellDamageBonus: 0

View File

@ -67,11 +67,11 @@ public class CastBarHandler : MonoBehaviour
ToggleCastBarVisibility(false);
}
public void ShowCastBar(BaseAbility ability, Action abilityExecution)
public void ShowCastBar(BaseAbility ability, Action abilityExecution, float castTime)
{
currentlyWaitingAbilityExecution = abilityExecution;
currentAbility = ability;
currentCastTime = ability.castTime;
currentCastTime = castTime;
castBarIcon.sprite = ability.Icon;
StartCoroutine(FillImageOverTime(ability.castTime + 0.05f));
}

View File

@ -1,3 +1,4 @@
using Kryz.CharacterStats.Examples;
using System;
using System.Collections;
using System.Collections.Generic;
@ -12,24 +13,38 @@ public class CastingStateController : MonoBehaviour
public LayerMask movementMask;
private MovementSpeedModifierEffectInstance movementSpeedModifier;
Camera cam;
Ray ray;
RaycastHit hit;
ProjectileSpawnLocationController projectileSpawnLocationController;
private Camera cam;
private Ray ray;
private RaycastHit hit;
private ProjectileSpawnLocationController projectileSpawnLocationController;
private CharacterStats stats;
public UnityEvent<BaseAbility> OnAbilityQueued = new UnityEvent<BaseAbility>();
private void Awake()
{
cam = Camera.main;
movementSpeedModifier = playerMovement.GetComponent<MovementSpeedModifierEffectInstance>();
stats = playerMovement.GetComponent<CharacterStats>();
projectileSpawnLocationController = transform.root.GetComponentInChildren<ProjectileSpawnLocationController>();
}
private void Start()
{
/// <summary>
/// Calculate current attack interval based on base speed and modifiers
/// </summary>
private float GetCurrentAttackInterval()
{
float totalAttackSpeed = GameConstants.CharacterStatsBalancing.BaseAttacksPerSecond * (1f + (MathHelpers.NormalizePercentage(stats.AttackSpeed.Value) / 100f));
float interval = 1f / totalAttackSpeed;
return Mathf.Max(interval, GameConstants.CharacterStatsBalancing.FastestAttacksPerSecond);
}
/// <summary>
/// Get current attacks per second
/// </summary>
public float GetCurrentAttacksPerSecond()
{
return 1f / GetCurrentAttackInterval();
}
public void RequestAbilityCast(BaseAbility ability, Action abilityExecution)
@ -39,12 +54,12 @@ public class CastingStateController : MonoBehaviour
if (!ability.castableWhileMoving)
{
movementSpeedModifier.ToggleCastPenalty(true);
//playerMovement.ToggleAgentMoving(true);
}
OnAbilityQueued.Invoke(ability);
StartCoroutine(Casting(ability, abilityExecution));
}
public void RequestAbilityChannel(BaseAbility channeledAbility, Action abilityChannelExecution)
{
if (isCasting) return;
@ -52,7 +67,6 @@ public class CastingStateController : MonoBehaviour
if (!channeledAbility.castableWhileMoving)
{
movementSpeedModifier.ToggleCastPenalty(true);
//playerMovement.ToggleAgentMoving(true);
}
OnAbilityQueued.Invoke(channeledAbility);
@ -62,12 +76,15 @@ public class CastingStateController : MonoBehaviour
private IEnumerator Casting(BaseAbility ability, Action abilityExecution)
{
isCasting = true;
CastBarHandler.Instance.ShowCastBar(ability, abilityExecution);
// Use player attack speed instead of ability cast time
float castTime = GetCurrentAttackInterval();
CastBarHandler.Instance.ShowCastBar(ability, abilityExecution, castTime);
playerMovement.InstantFaceCast(projectileSpawnLocationController.GetLookat());
//cast animation
yield return new WaitForSeconds(ability.castTime);
// Wait for the calculated attack interval
yield return new WaitForSeconds(castTime);
if (!GameConstants.Animation.IsAnimationEventBasedAbility(ability.animationType))
{
@ -75,29 +92,26 @@ public class CastingStateController : MonoBehaviour
}
isCasting = false;
if (!ability.castableWhileMoving)
{
movementSpeedModifier.ToggleCastPenalty(false);
//playerMovement.ToggleAgentMoving(false);
}
//Debug.Log("$$sCastbar Done casting");
}
private IEnumerator Channeling(BaseAbility channeledAbility, Action abilityChannelExecution)
{
isCasting = true;
// For channeled abilities, you might want different behavior
// This maintains the original channeling logic
CastBarHandler.Instance.ShowChannelingBar(channeledAbility, abilityChannelExecution);
playerMovement.InstantFaceCast(projectileSpawnLocationController.GetLookat());
abilityChannelExecution.Invoke();
if (!channeledAbility.castableWhileMoving)
{
movementSpeedModifier.ToggleCastPenalty(true);
//playerMovement.ToggleAgentMoving(true);
}
yield return null;
@ -107,15 +121,14 @@ public class CastingStateController : MonoBehaviour
{
isCasting = false;
movementSpeedModifier.ToggleCastPenalty(false);
//playerMovement.ToggleAgentMoving(false);
}
public void ResetCastingOnMeleeAnimationEvent()
{
isCasting = false;
movementSpeedModifier.ToggleCastPenalty(false);
//playerMovement.ToggleAgentMoving(false);
StopAllCoroutines();
//Debug.Log("$$ResetCastingState On Event");
}
}
}

View File

@ -6,5 +6,6 @@ using UnityEngine;
public class StatInfluence
{
public GameTag statTag;
public CharacterStatType statType;
public float percentInfluence;
}

View File

@ -1,3 +1,4 @@
using Kryz.CharacterStats;
using UnityEngine;
using static GameConstants.EnemySpawning;
@ -167,6 +168,8 @@ public static class GameConstants
public const float MaximumPercentDamageReductionFromMagicResistance = 0.75f;
public const float MaximumPercentCooldownReduction = 0.9f;
public const float MaximumPercentDamageReductionFromBlock = 0.75f;
public const float BaseAttacksPerSecond = 1f;
public const float FastestAttacksPerSecond = 0.25f;
public const float BaseMaxHealthGrowthPerLevel = 0.2f;
@ -240,6 +243,7 @@ public static class GameConstants
public const float MovementSpeedCastingPenalty = -0.75f;
public const float MovementSpeedLowestCap = 0.25f;
public const float BossMovementSpeedBaseLowestPercentCap = 0.8f; //bosses can only be lowered to 80% of their normal speed: 2.85 base speed == 2.28 minimum speed
}
public static class GameBalancing
{
@ -435,4 +439,11 @@ public static class GameConstants
return HuntersInn;
}
}
public static class EquipmentStatRules
{
public const int ItemTotalStats = 4;
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1b226c360854c4b4f8bdce0961d34302
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,166 @@
using Kryz.CharacterStats.Examples;
using System.Collections.Generic;
using UnityEngine;
public class EquippableItemGenerator : MonoBehaviour
{
public List<Sprite> HelmetIcons = new List<Sprite>();
public List<Sprite> ShoulderIcons = new List<Sprite>();
public List<Sprite> ChestIcons = new List<Sprite>();
public List<Sprite> BeltIcons = new List<Sprite>();
public List<Sprite> LegsIcons = new List<Sprite>();
public List<Sprite> BracersIcons = new List<Sprite>();
public List<Sprite> GlovesIcons = new List<Sprite>();
public List<Sprite> BootsIcons = new List<Sprite>();
public List<Sprite> StaffIcons = new List<Sprite>();
public List<Sprite> SpearIcons = new List<Sprite>();
public List<Sprite> ScytheIcons = new List<Sprite>();
public List<Sprite> HammerIcons = new List<Sprite>();
public List<Sprite> BowIcons = new List<Sprite>();
public List<Sprite> CrossbowIcons = new List<Sprite>();
public List<Sprite> AxeIcons = new List<Sprite>();
public List<Sprite> SwordIcons = new List<Sprite>();
public List<Sprite> ShieldIcons = new List<Sprite>();
public List<Sprite> DaggerIcons = new List<Sprite>();
public List<Sprite> BookIcons = new List<Sprite>();
[ContextMenu("Update Lists From Resources")]
private void UpdateListsFromResources()
{
// Armor pieces
HelmetIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Helmets"))
{
HelmetIcons.Add(sprite);
}
ShoulderIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Shoulders"))
{
ShoulderIcons.Add(sprite);
}
ChestIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Chests"))
{
ChestIcons.Add(sprite);
}
BeltIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Belts"))
{
BeltIcons.Add(sprite);
}
LegsIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Legs"))
{
LegsIcons.Add(sprite);
}
BracersIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Bracers"))
{
BracersIcons.Add(sprite);
}
GlovesIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Gloves"))
{
GlovesIcons.Add(sprite);
}
BootsIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Armor/Boots"))
{
BootsIcons.Add(sprite);
}
// Two-handed weapons
StaffIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Staffs"))
{
StaffIcons.Add(sprite);
}
SpearIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Spears"))
{
SpearIcons.Add(sprite);
}
ScytheIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Scythes"))
{
ScytheIcons.Add(sprite);
}
HammerIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Hammers"))
{
HammerIcons.Add(sprite);
}
BowIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Bows"))
{
BowIcons.Add(sprite);
}
CrossbowIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Crossbows"))
{
CrossbowIcons.Add(sprite);
}
AxeIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Axes"))
{
AxeIcons.Add(sprite);
}
// One-handed weapons
SwordIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Swords"))
{
SwordIcons.Add(sprite);
}
ShieldIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Shields"))
{
ShieldIcons.Add(sprite);
}
DaggerIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Daggers"))
{
DaggerIcons.Add(sprite);
}
BookIcons.Clear();
foreach (Sprite sprite in Resources.LoadAll<Sprite>("Weapons/Books"))
{
BookIcons.Add(sprite);
}
Debug.Log("Updated all icon lists from Resources folders");
}
public EquippableItemInstance GenerateEquippableItemInstance()
{
EquippableItemInstance generatedItem = new EquippableItemInstance();
//Do stuff
return generatedItem;
}
public EquippableItemInstance GenerateEquippableItemInstance(EquipmentType equipmentType)
{
EquippableItemInstance generatedItem = new EquippableItemInstance();
//Do stuff
return generatedItem;
}
}

View File

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