Phase 1: 复合属性修饰器管线与伤害结算系统
This commit is contained in:
Binary file not shown.
@@ -3,19 +3,19 @@
|
||||
"weaponCapacity": 6,
|
||||
"propMaxCount": -1,
|
||||
"playerEntity": "Player",
|
||||
"absorbSpeed": 15000,
|
||||
"collectDistance": 350,
|
||||
"defaultAbsorbRange": 3000,
|
||||
"weaponRecycleRate": 300,
|
||||
"absorbSpeed": 12,
|
||||
"collectDistance": 0.5,
|
||||
"defaultAbsorbRange": 4,
|
||||
"weaponRecycleRate": 0.3,
|
||||
"shopGoodsCount": 4,
|
||||
"shopPriceRandomPercent": 50,
|
||||
"shopPriceRandomPercent": 0.05,
|
||||
"shopMinPrice": 1,
|
||||
"levelUpRefreshPriceBase": 2,
|
||||
"levelUpRewardCount": 4,
|
||||
"spawnEnemyMaxCount": 5000,
|
||||
"enemySpawnDistanceMin": 14000,
|
||||
"enemySpawnDistanceMax": 18000,
|
||||
"weaponRotateSpeed": 5000,
|
||||
"enemySpawnDistanceMin": 14,
|
||||
"enemySpawnDistanceMax": 18,
|
||||
"weaponRotateSpeed": 5,
|
||||
"weaponBaseCrit": 0.05,
|
||||
"playerGroupName": "Player",
|
||||
"enemyGroupName": "Enemy",
|
||||
|
||||
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
"price": 120,
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 5,
|
||||
"value": 10,
|
||||
"modifierType": 100
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"modifierType": 200
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 5,
|
||||
"value": 11,
|
||||
"modifierType": 100
|
||||
}
|
||||
@@ -88,11 +88,11 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 14,
|
||||
"value": 800,
|
||||
"value": 0.8,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 5,
|
||||
"value": 10,
|
||||
"modifierType": 100
|
||||
}
|
||||
@@ -107,11 +107,11 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 13,
|
||||
"value": 10,
|
||||
"value": 0.1,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 5,
|
||||
"value": 12,
|
||||
"modifierType": 100
|
||||
},
|
||||
@@ -144,13 +144,13 @@
|
||||
"price": 433,
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 6,
|
||||
"value": 44,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 13,
|
||||
"value": 27,
|
||||
"value": 0.27,
|
||||
"modifierType": 100
|
||||
}
|
||||
],
|
||||
@@ -168,7 +168,7 @@
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 6,
|
||||
"value": 7,
|
||||
"modifierType": 100
|
||||
},
|
||||
@@ -201,7 +201,7 @@
|
||||
"price": 341,
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 6,
|
||||
"value": 57,
|
||||
"modifierType": 100
|
||||
}
|
||||
@@ -221,7 +221,7 @@
|
||||
},
|
||||
{
|
||||
"statType": 9,
|
||||
"value": 27,
|
||||
"value": 0.27,
|
||||
"modifierType": 100
|
||||
}
|
||||
],
|
||||
@@ -235,12 +235,12 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 9,
|
||||
"value": 5,
|
||||
"value": 0.05,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 13,
|
||||
"value": 7,
|
||||
"value": 0.07,
|
||||
"modifierType": 100
|
||||
}
|
||||
],
|
||||
@@ -254,7 +254,7 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 14,
|
||||
"value": 600,
|
||||
"value": 0.6,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
@@ -273,7 +273,7 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 14,
|
||||
"value": 1600,
|
||||
"value": 1.6,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
@@ -292,7 +292,7 @@
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 13,
|
||||
"value": 28,
|
||||
"value": 0.28,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
@@ -301,7 +301,7 @@
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 5,
|
||||
"value": 24,
|
||||
"modifierType": 100
|
||||
}
|
||||
@@ -329,7 +329,7 @@
|
||||
"price": 204,
|
||||
"statModifierConfigs": [
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 7,
|
||||
"value": 17,
|
||||
"modifierType": 100
|
||||
},
|
||||
@@ -353,13 +353,13 @@
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 7,
|
||||
"value": 19,
|
||||
"modifierType": 100
|
||||
},
|
||||
{
|
||||
"statType": 13,
|
||||
"value": 20,
|
||||
"value": 0.2,
|
||||
"modifierType": 100
|
||||
}
|
||||
],
|
||||
@@ -377,7 +377,7 @@
|
||||
"modifierType": 200
|
||||
},
|
||||
{
|
||||
"statType": 4,
|
||||
"statType": 7,
|
||||
"value": 29,
|
||||
"modifierType": 100
|
||||
},
|
||||
|
||||
Binary file not shown.
@@ -32,7 +32,7 @@
|
||||
{
|
||||
"statType": 4,
|
||||
"name": "伤害",
|
||||
"defaultBaseValue": 0,
|
||||
"defaultBaseValue": 1,
|
||||
"hasMin": true,
|
||||
"minValue": -1,
|
||||
"hasMax": false,
|
||||
@@ -72,7 +72,7 @@
|
||||
{
|
||||
"statType": 8,
|
||||
"name": "攻击速度",
|
||||
"defaultBaseValue": 0,
|
||||
"defaultBaseValue": 1,
|
||||
"hasMin": true,
|
||||
"minValue": -0.8,
|
||||
"hasMax": false,
|
||||
|
||||
Binary file not shown.
@@ -5,14 +5,14 @@
|
||||
"rarity": 1,
|
||||
"price": 120,
|
||||
"attack": 100,
|
||||
"cooldown": 1500,
|
||||
"cooldown": 1.5,
|
||||
"damageType": 1,
|
||||
"attackRange": 5000,
|
||||
"attackRange": 5,
|
||||
"attackSoundId": 10000,
|
||||
"parameters": [
|
||||
[
|
||||
1,
|
||||
2000
|
||||
2
|
||||
]
|
||||
],
|
||||
"statModifierConfigs": [],
|
||||
@@ -25,9 +25,9 @@
|
||||
"rarity": 1,
|
||||
"price": 130,
|
||||
"attack": 120,
|
||||
"cooldown": 1000,
|
||||
"cooldown": 1,
|
||||
"damageType": 2,
|
||||
"attackRange": 15000,
|
||||
"attackRange": 15,
|
||||
"attackSoundId": 10000,
|
||||
"parameters": [],
|
||||
"statModifierConfigs": [],
|
||||
@@ -40,9 +40,9 @@
|
||||
"rarity": 1,
|
||||
"price": 100,
|
||||
"attack": 150,
|
||||
"cooldown": 2000,
|
||||
"cooldown": 2,
|
||||
"damageType": 1,
|
||||
"attackRange": 5000,
|
||||
"attackRange": 5,
|
||||
"attackSoundId": 10000,
|
||||
"parameters": [
|
||||
[
|
||||
@@ -60,14 +60,14 @@
|
||||
"rarity": 1,
|
||||
"price": 150,
|
||||
"attack": 80,
|
||||
"cooldown": 3000,
|
||||
"cooldown": 3,
|
||||
"damageType": 4,
|
||||
"attackRange": 12000,
|
||||
"attackRange": 12,
|
||||
"attackSoundId": 10000,
|
||||
"parameters": [
|
||||
[
|
||||
1,
|
||||
3000
|
||||
3
|
||||
]
|
||||
],
|
||||
"statModifierConfigs": [],
|
||||
@@ -80,22 +80,22 @@
|
||||
"rarity": 1,
|
||||
"price": 100,
|
||||
"attack": 100,
|
||||
"cooldown": 1500,
|
||||
"cooldown": 1.5,
|
||||
"damageType": 1,
|
||||
"attackRange": 5000,
|
||||
"attackRange": 5,
|
||||
"attackSoundId": 10000,
|
||||
"parameters": [
|
||||
[
|
||||
3,
|
||||
700
|
||||
0.7
|
||||
],
|
||||
[
|
||||
4,
|
||||
4500
|
||||
4.5
|
||||
],
|
||||
[
|
||||
5,
|
||||
500
|
||||
0.5
|
||||
]
|
||||
],
|
||||
"statModifierConfigs": [],
|
||||
|
||||
@@ -32,32 +32,21 @@ namespace SepCore.Entity
|
||||
|
||||
public int Price => Config.Price;
|
||||
|
||||
public float Cooldown => Config.Cooldown / 1000f;
|
||||
public float Cooldown => Config.Cooldown;
|
||||
|
||||
public float AttackRange => Config.AttackRange / 1000f;
|
||||
public float AttackRange => Config.AttackRange;
|
||||
|
||||
public int AttackSoundId => Config.AttackSoundId;
|
||||
|
||||
public IReadOnlyDictionary<ParamType, int> Parameters => Config.Parameters;
|
||||
public IReadOnlyDictionary<ParamType, float> Parameters => Config.Parameters;
|
||||
|
||||
public IReadOnlyList<StatModifierConfig> Modifiers => Config.StatModifierConfigs;
|
||||
|
||||
public int GetParam(ParamType paramType)
|
||||
{
|
||||
if (Config.Parameters.TryGetValue(paramType, out int value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public float GetParamFloat(ParamType paramType)
|
||||
{
|
||||
if (Config.Parameters.TryGetValue(paramType, out int val))
|
||||
if (Config.Parameters.TryGetValue(paramType, out float val))
|
||||
{
|
||||
return paramType == ParamType.SectorAngle ? val : val / 1000f;
|
||||
return val;
|
||||
}
|
||||
|
||||
Log.Error("Weapon parameter '{0}' not found for weapon '{1}'.", paramType, Config.WeaponType);
|
||||
@@ -65,4 +54,3 @@ namespace SepCore.Entity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,19 +20,19 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
||||
PropMaxCount = _buf.ReadInt();
|
||||
PlayerEntity = _buf.ReadString();
|
||||
PlayerEntity_Ref = null;
|
||||
AbsorbSpeed = _buf.ReadInt();
|
||||
CollectDistance = _buf.ReadInt();
|
||||
DefaultAbsorbRange = _buf.ReadInt();
|
||||
WeaponRecycleRate = _buf.ReadInt();
|
||||
AbsorbSpeed = _buf.ReadFloat();
|
||||
CollectDistance = _buf.ReadFloat();
|
||||
DefaultAbsorbRange = _buf.ReadFloat();
|
||||
WeaponRecycleRate = _buf.ReadFloat();
|
||||
ShopGoodsCount = _buf.ReadInt();
|
||||
ShopPriceRandomPercent = _buf.ReadInt();
|
||||
ShopPriceRandomPercent = _buf.ReadFloat();
|
||||
ShopMinPrice = _buf.ReadInt();
|
||||
LevelUpRefreshPriceBase = _buf.ReadInt();
|
||||
LevelUpRewardCount = _buf.ReadInt();
|
||||
SpawnEnemyMaxCount = _buf.ReadInt();
|
||||
EnemySpawnDistanceMin = _buf.ReadInt();
|
||||
EnemySpawnDistanceMax = _buf.ReadInt();
|
||||
WeaponRotateSpeed = _buf.ReadInt();
|
||||
EnemySpawnDistanceMin = _buf.ReadFloat();
|
||||
EnemySpawnDistanceMax = _buf.ReadFloat();
|
||||
WeaponRotateSpeed = _buf.ReadFloat();
|
||||
WeaponBaseCrit = _buf.ReadFloat();
|
||||
PlayerGroupName = _buf.ReadString();
|
||||
EnemyGroupName = _buf.ReadString();
|
||||
@@ -75,19 +75,19 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 自动收集速度
|
||||
/// </summary>
|
||||
public readonly int AbsorbSpeed;
|
||||
public readonly float AbsorbSpeed;
|
||||
/// <summary>
|
||||
/// 自动收集距离
|
||||
/// </summary>
|
||||
public readonly int CollectDistance;
|
||||
public readonly float CollectDistance;
|
||||
/// <summary>
|
||||
/// 默认吸收距离
|
||||
/// </summary>
|
||||
public readonly int DefaultAbsorbRange;
|
||||
public readonly float DefaultAbsorbRange;
|
||||
/// <summary>
|
||||
/// 武器回收价比例
|
||||
/// </summary>
|
||||
public readonly int WeaponRecycleRate;
|
||||
public readonly float WeaponRecycleRate;
|
||||
/// <summary>
|
||||
/// 商店商品数量
|
||||
/// </summary>
|
||||
@@ -95,7 +95,7 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 商品价格浮动
|
||||
/// </summary>
|
||||
public readonly int ShopPriceRandomPercent;
|
||||
public readonly float ShopPriceRandomPercent;
|
||||
/// <summary>
|
||||
/// 商品最低价
|
||||
/// </summary>
|
||||
@@ -115,15 +115,15 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 敌人生成最小距离
|
||||
/// </summary>
|
||||
public readonly int EnemySpawnDistanceMin;
|
||||
public readonly float EnemySpawnDistanceMin;
|
||||
/// <summary>
|
||||
/// 敌人生成最大距离
|
||||
/// </summary>
|
||||
public readonly int EnemySpawnDistanceMax;
|
||||
public readonly float EnemySpawnDistanceMax;
|
||||
/// <summary>
|
||||
/// 武器旋转速度
|
||||
/// </summary>
|
||||
public readonly int WeaponRotateSpeed;
|
||||
public readonly float WeaponRotateSpeed;
|
||||
/// <summary>
|
||||
/// 武器基本暴击率
|
||||
/// </summary>
|
||||
|
||||
@@ -45,19 +45,19 @@ public partial class TbGlobalConfig
|
||||
/// <summary>
|
||||
/// 自动收集速度
|
||||
/// </summary>
|
||||
public int AbsorbSpeed => _data.AbsorbSpeed;
|
||||
public float AbsorbSpeed => _data.AbsorbSpeed;
|
||||
/// <summary>
|
||||
/// 自动收集距离
|
||||
/// </summary>
|
||||
public int CollectDistance => _data.CollectDistance;
|
||||
public float CollectDistance => _data.CollectDistance;
|
||||
/// <summary>
|
||||
/// 默认吸收距离
|
||||
/// </summary>
|
||||
public int DefaultAbsorbRange => _data.DefaultAbsorbRange;
|
||||
public float DefaultAbsorbRange => _data.DefaultAbsorbRange;
|
||||
/// <summary>
|
||||
/// 武器回收价比例
|
||||
/// </summary>
|
||||
public int WeaponRecycleRate => _data.WeaponRecycleRate;
|
||||
public float WeaponRecycleRate => _data.WeaponRecycleRate;
|
||||
/// <summary>
|
||||
/// 商店商品数量
|
||||
/// </summary>
|
||||
@@ -65,7 +65,7 @@ public partial class TbGlobalConfig
|
||||
/// <summary>
|
||||
/// 商品价格浮动
|
||||
/// </summary>
|
||||
public int ShopPriceRandomPercent => _data.ShopPriceRandomPercent;
|
||||
public float ShopPriceRandomPercent => _data.ShopPriceRandomPercent;
|
||||
/// <summary>
|
||||
/// 商品最低价
|
||||
/// </summary>
|
||||
@@ -85,15 +85,15 @@ public partial class TbGlobalConfig
|
||||
/// <summary>
|
||||
/// 敌人生成最小距离
|
||||
/// </summary>
|
||||
public int EnemySpawnDistanceMin => _data.EnemySpawnDistanceMin;
|
||||
public float EnemySpawnDistanceMin => _data.EnemySpawnDistanceMin;
|
||||
/// <summary>
|
||||
/// 敌人生成最大距离
|
||||
/// </summary>
|
||||
public int EnemySpawnDistanceMax => _data.EnemySpawnDistanceMax;
|
||||
public float EnemySpawnDistanceMax => _data.EnemySpawnDistanceMax;
|
||||
/// <summary>
|
||||
/// 武器旋转速度
|
||||
/// </summary>
|
||||
public int WeaponRotateSpeed => _data.WeaponRotateSpeed;
|
||||
public float WeaponRotateSpeed => _data.WeaponRotateSpeed;
|
||||
/// <summary>
|
||||
/// 武器基本暴击率
|
||||
/// </summary>
|
||||
|
||||
@@ -21,11 +21,11 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
||||
Rarity = (Rarity)_buf.ReadInt();
|
||||
Price = _buf.ReadInt();
|
||||
Attack = _buf.ReadInt();
|
||||
Cooldown = _buf.ReadInt();
|
||||
Cooldown = _buf.ReadFloat();
|
||||
DamageType = (DamageType)_buf.ReadInt();
|
||||
AttackRange = _buf.ReadInt();
|
||||
AttackRange = _buf.ReadFloat();
|
||||
AttackSoundId = _buf.ReadInt();
|
||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Parameters = new System.Collections.Generic.Dictionary<ParamType, int>(n0 * 3 / 2);for(var i0 = 0 ; i0 < n0 ; i0++) { ParamType _k0; _k0 = (ParamType)_buf.ReadInt(); int _v0; _v0 = _buf.ReadInt(); Parameters.Add(_k0, _v0);}}
|
||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);Parameters = new System.Collections.Generic.Dictionary<ParamType, float>(n0 * 3 / 2);for(var i0 = 0 ; i0 < n0 ; i0++) { ParamType _k0; _k0 = (ParamType)_buf.ReadInt(); float _v0; _v0 = _buf.ReadFloat(); Parameters.Add(_k0, _v0);}}
|
||||
{int n0 = System.Math.Min(_buf.ReadSize(), _buf.Size);StatModifierConfigs = new System.Collections.Generic.List<StatModifierConfig>(n0);for(var i0 = 0 ; i0 < n0 ; i0++) { StatModifierConfig _e0; _e0 = StatModifierConfig.DeserializeStatModifierConfig(_buf); StatModifierConfigs.Add(_e0);}}
|
||||
Prefab = _buf.ReadString();
|
||||
Prefab_Ref = null;
|
||||
@@ -61,7 +61,7 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 冷却
|
||||
/// </summary>
|
||||
public readonly int Cooldown;
|
||||
public readonly float Cooldown;
|
||||
/// <summary>
|
||||
/// 伤害类型
|
||||
/// </summary>
|
||||
@@ -69,7 +69,7 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 攻击范围
|
||||
/// </summary>
|
||||
public readonly int AttackRange;
|
||||
public readonly float AttackRange;
|
||||
/// <summary>
|
||||
/// 攻击音效编号
|
||||
/// </summary>
|
||||
@@ -77,7 +77,7 @@ public sealed partial class WeaponConfig : Luban.BeanBase
|
||||
/// <summary>
|
||||
/// 参数类型
|
||||
/// </summary>
|
||||
public readonly System.Collections.Generic.Dictionary<ParamType, int> Parameters;
|
||||
public readonly System.Collections.Generic.Dictionary<ParamType, float> Parameters;
|
||||
public readonly System.Collections.Generic.List<StatModifierConfig> StatModifierConfigs;
|
||||
/// <summary>
|
||||
/// 预制体
|
||||
|
||||
@@ -51,13 +51,39 @@ namespace SepCore.CustomUtility
|
||||
string colorTag = statModifierConfig.Value >= 0 ? "green" : "red";
|
||||
string sign = statModifierConfig.Value >= 0 ? "+" : "";
|
||||
|
||||
if (statModifierConfig.ModifierType == ModifierType.PercentAdd)
|
||||
StatConfig statConfig = null;
|
||||
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
||||
{
|
||||
statConfig = GameEntry.Luban.Get<StatConfig>(index);
|
||||
}
|
||||
|
||||
if (statConfig != null && !string.IsNullOrEmpty(statConfig.Name))
|
||||
{
|
||||
name = statConfig.Name;
|
||||
}
|
||||
|
||||
// 是否以百分比展示:由 StatConfig.IsPercentDisplay 真正驱动(Luban 未就绪时 fallback 到已知百分比属性)
|
||||
bool isPercentDisplay = statConfig != null
|
||||
? statConfig.IsPercentDisplay
|
||||
: (statModifierConfig.StatType is StatType.DamagePercent or StatType.AttackSpeed or StatType.CritChance or StatType.Dodge);
|
||||
|
||||
// 1. 修饰类型判断
|
||||
bool isPercentAdd = statModifierConfig.ModifierType == ModifierType.PercentAdd;
|
||||
bool isPercentMult = statModifierConfig.ModifierType == ModifierType.PercentMult;
|
||||
bool isFlat = statModifierConfig.ModifierType == ModifierType.Flat;
|
||||
|
||||
// 2. UI 百分比展示裁决:
|
||||
// - 任意属性的 PercentAdd 词条属于比例加成(如 攻速 +19%、伤害 +5%、移速 +15%)
|
||||
// - 或属性自身在配置表中标记为百分比显示(IsPercentDisplay 为 true,如暴击率、闪避率等),其 Flat 加成应以百分比呈现(如 0.1 -> +10%)
|
||||
bool shouldDisplayAsPercent = isPercentAdd || (isFlat && isPercentDisplay);
|
||||
|
||||
if (shouldDisplayAsPercent)
|
||||
{
|
||||
float displayPercent = statModifierConfig.Value * 100f;
|
||||
return $"{name}: <color={colorTag}>{sign}{displayPercent:0.##}%</color>";
|
||||
}
|
||||
|
||||
if (statModifierConfig.ModifierType == ModifierType.PercentMult)
|
||||
if (isPercentMult)
|
||||
{
|
||||
return $"{name}: <color={colorTag}>x{statModifierConfig.Value:0.##}</color>";
|
||||
}
|
||||
@@ -89,8 +115,8 @@ namespace SepCore.CustomUtility
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateWeaponDescription(weaponConfig.Attack, weaponConfig.Cooldown / 1000f,
|
||||
weaponConfig.AttackRange / 1000f, weaponConfig.StatModifierConfigs, weaponConfig.Parameters);
|
||||
return CreateWeaponDescription(weaponConfig.Attack, weaponConfig.Cooldown,
|
||||
weaponConfig.AttackRange, weaponConfig.StatModifierConfigs, weaponConfig.Parameters);
|
||||
}
|
||||
|
||||
public static string CreateWeaponDescription(WeaponBase weapon)
|
||||
@@ -125,7 +151,7 @@ namespace SepCore.CustomUtility
|
||||
}
|
||||
|
||||
private static string CreateWeaponDescription(int attack, float cooldown, float attackRange,
|
||||
IEnumerable<StatModifierConfig> modifiers, IReadOnlyDictionary<ParamType, int> parameters)
|
||||
IEnumerable<StatModifierConfig> modifiers, IReadOnlyDictionary<ParamType, float> parameters)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"攻击: {attack}");
|
||||
@@ -152,7 +178,7 @@ namespace SepCore.CustomUtility
|
||||
|
||||
string valStr = kvp.Key == ParamType.SectorAngle
|
||||
? kvp.Value.ToString()
|
||||
: (kvp.Value / 1000f).ToString("0.##");
|
||||
: kvp.Value.ToString("0.##");
|
||||
|
||||
sb.AppendLine($"{paramName}: {valStr}");
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace SepCore.Battle
|
||||
{
|
||||
private readonly List<StatModifier> _modifiers = new();
|
||||
private float _baseValue;
|
||||
private bool _isBaseValueExplicitlySet;
|
||||
private bool _isDirty = true;
|
||||
private float _cachedValue;
|
||||
private StatConfig _config;
|
||||
@@ -26,6 +27,11 @@ namespace SepCore.Battle
|
||||
if (_config == null && GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
||||
{
|
||||
_config = GameEntry.Luban.Get<StatConfig>((int)Type);
|
||||
if (!_isBaseValueExplicitlySet && _config != null)
|
||||
{
|
||||
_baseValue = _config.DefaultBaseValue;
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return _config;
|
||||
@@ -33,15 +39,35 @@ namespace SepCore.Battle
|
||||
set
|
||||
{
|
||||
_config = value;
|
||||
if (!_isBaseValueExplicitlySet && _config != null)
|
||||
{
|
||||
_baseValue = _config.DefaultBaseValue;
|
||||
}
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public float BaseValue
|
||||
{
|
||||
get => _baseValue;
|
||||
get
|
||||
{
|
||||
if (!_isBaseValueExplicitlySet)
|
||||
{
|
||||
if (Config != null)
|
||||
{
|
||||
_baseValue = Config.DefaultBaseValue;
|
||||
}
|
||||
else if (Type is StatType.DamagePercent or StatType.AttackSpeed)
|
||||
{
|
||||
_baseValue = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return _baseValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isBaseValueExplicitlySet = true;
|
||||
if (Math.Abs(_baseValue - value) > 1e-6f)
|
||||
{
|
||||
_baseValue = value;
|
||||
@@ -69,8 +95,21 @@ namespace SepCore.Battle
|
||||
public CharacterStat(StatType type, float baseValue = 0f, StatConfig config = null)
|
||||
{
|
||||
Type = type;
|
||||
_baseValue = baseValue;
|
||||
_config = config;
|
||||
if (Math.Abs(baseValue) > 1e-6f)
|
||||
{
|
||||
_baseValue = baseValue;
|
||||
_isBaseValueExplicitlySet = true;
|
||||
}
|
||||
else if (_config != null)
|
||||
{
|
||||
_baseValue = _config.DefaultBaseValue;
|
||||
_isBaseValueExplicitlySet = true;
|
||||
}
|
||||
else if (type is StatType.DamagePercent or StatType.AttackSpeed)
|
||||
{
|
||||
_baseValue = 1.0f;
|
||||
}
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
@@ -154,7 +193,7 @@ namespace SepCore.Battle
|
||||
}
|
||||
}
|
||||
|
||||
float finalValue = ((_baseValue + sumFlat) * (1f + sumPercentAdd)) * prodPercentMult;
|
||||
float finalValue = ((BaseValue + sumFlat) * (1f + sumPercentAdd)) * prodPercentMult;
|
||||
return ClampStat(finalValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,15 @@ namespace SepCore.Battle
|
||||
float maxDodge = 0.8f;
|
||||
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
||||
{
|
||||
maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
|
||||
StatConfig dodgeConfig = GameEntry.Luban.Get<StatConfig>((int)StatType.Dodge);
|
||||
if (dodgeConfig != null && dodgeConfig.HasMax)
|
||||
{
|
||||
maxDodge = dodgeConfig.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
|
||||
}
|
||||
}
|
||||
|
||||
dodgeChance = Mathf.Clamp(dodgeChance, 0f, maxDodge);
|
||||
@@ -55,9 +63,11 @@ namespace SepCore.Battle
|
||||
// 3. 全局百分比增伤放大 (DamagePercent)
|
||||
float damagePercent = context.AttackerPipeline != null
|
||||
? context.AttackerPipeline.GetValue(StatType.DamagePercent)
|
||||
: 0f;
|
||||
float amplifiedDamage = rawDamage * (1f + damagePercent);
|
||||
amplifiedDamage = Mathf.Max(amplifiedDamage, 1f);
|
||||
: 1f;
|
||||
// 允许百分比降至 0,防止负向修饰(如 -100% 增伤/重度虚弱)被错误重置为 1 倍全额伤害
|
||||
damagePercent = Mathf.Max(0f, damagePercent);
|
||||
float amplifiedDamage = rawDamage * damagePercent;
|
||||
amplifiedDamage = Mathf.Max(amplifiedDamage, 0f);
|
||||
|
||||
// 4. 暴击判定 (CritChance & CritMultiplier)
|
||||
float attackerCrit = context.AttackerPipeline != null
|
||||
@@ -65,12 +75,16 @@ namespace SepCore.Battle
|
||||
: 0f;
|
||||
|
||||
float baseCrit = context.BaseCritChance;
|
||||
if (baseCrit <= 0f && GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
||||
float maxCrit = 0.9f;
|
||||
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
||||
{
|
||||
baseCrit = GameEntry.Luban.Global.WeaponBaseCrit;
|
||||
StatConfig critConfig = GameEntry.Luban.Get<StatConfig>((int)StatType.CritChance);
|
||||
if (critConfig != null && critConfig.HasMax)
|
||||
{
|
||||
maxCrit = critConfig.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, 0.9f);
|
||||
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, maxCrit);
|
||||
bool isCrit = totalCritChance > 0f && Random.value < totalCritChance;
|
||||
|
||||
float critMultiplier = context.CritMultiplier;
|
||||
|
||||
@@ -8,13 +8,11 @@ namespace SepCore.Components
|
||||
private Player _player;
|
||||
private Transform _target;
|
||||
|
||||
private float AbsorbRangeBase => GameEntry.Luban.Global.DefaultAbsorbRange / 1000f;
|
||||
private float AbsorbSpeed => GameEntry.Luban.Global.AbsorbSpeed / 1000f;
|
||||
private float CollectDistance => GameEntry.Luban.Global.CollectDistance / 1000f;
|
||||
public float AbsorbRange => GameEntry.Luban.Global.DefaultAbsorbRange;
|
||||
public float AbsorbSpeed => GameEntry.Luban.Global.AbsorbSpeed;
|
||||
public float CollectDistance => GameEntry.Luban.Global.CollectDistance;
|
||||
|
||||
private float AbsorbRange => AbsorbRangeBase;
|
||||
|
||||
public void OnInit(Player player, StatComponent statComponent)
|
||||
public void OnInit(Player player)
|
||||
{
|
||||
_player = player;
|
||||
_target = player.CachedTransform;
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ namespace SepCore.EnemyManager
|
||||
|
||||
_isStopped = false;
|
||||
_spawnEnemyMaxCount = GameEntry.Luban.Global.SpawnEnemyMaxCount;
|
||||
float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin / 1000f;
|
||||
float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax / 1000f;
|
||||
float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin;
|
||||
float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax;
|
||||
_player = player.CachedTransform;
|
||||
_spawnPositionStrategy = new RandomCircleSpawnStrategy(minDistance, maxDistance);
|
||||
|
||||
|
||||
+3
-3
@@ -23,19 +23,19 @@ namespace SepCore.Simulation
|
||||
private float ResolveAbsorbRange()
|
||||
{
|
||||
if (_playerAbsorbRange >= 0f) return _playerAbsorbRange;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.DefaultAbsorbRange / 1000f : 2f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.DefaultAbsorbRange : 2f;
|
||||
}
|
||||
|
||||
private float ResolveAbsorbSpeed()
|
||||
{
|
||||
if (_playerAbsorbSpeed >= 0f) return _playerAbsorbSpeed;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.AbsorbSpeed / 1000f : 8f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.AbsorbSpeed : 8f;
|
||||
}
|
||||
|
||||
private float ResolveCollectDistance()
|
||||
{
|
||||
if (_playerCollectDistance >= 0f) return _playerCollectDistance;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.CollectDistance / 1000f : 0.5f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.CollectDistance : 0.5f;
|
||||
}
|
||||
|
||||
[Header("敌人互斥参数")]
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace SepCore.Entity
|
||||
|
||||
_inputComponent.OnInit();
|
||||
_inputComponent.SetListening(true);
|
||||
_absorbComponent.OnInit(this, _statComponent);
|
||||
_absorbComponent.OnInit(this);
|
||||
_weaponEnabled = true;
|
||||
|
||||
_currentLevel = 0;
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace SepCore.Entity
|
||||
get
|
||||
{
|
||||
if (WeaponData == null) return 0f;
|
||||
float attackSpeed = _ownerAttributeProvider?.GetValue(StatType.AttackSpeed) ?? 0f;
|
||||
float speedFactor = Mathf.Max(0.2f, 1f + attackSpeed);
|
||||
float attackSpeed = _ownerAttributeProvider?.GetValue(StatType.AttackSpeed) ?? 1f;
|
||||
float speedFactor = Mathf.Max(0.2f, attackSpeed);
|
||||
return Mathf.Max(0.02f, WeaponData.Cooldown / speedFactor);
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,7 @@ namespace SepCore.Entity
|
||||
{
|
||||
if (WeaponData == null) return 0f;
|
||||
float rangeBonus = _ownerAttributeProvider?.GetValue(StatType.Range) ?? 0f;
|
||||
float effectiveBonus = rangeBonus >= 50f ? rangeBonus / 1000f : rangeBonus;
|
||||
return Mathf.Max(0.5f, WeaponData.AttackRange + effectiveBonus);
|
||||
return Mathf.Max(0.5f, WeaponData.AttackRange + rangeBonus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace SepCore.UI
|
||||
_selections.Clear();
|
||||
|
||||
int currentLevel = _currentLevel;
|
||||
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent / 1000f;
|
||||
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent;
|
||||
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
@@ -324,7 +324,7 @@ namespace SepCore.UI
|
||||
|
||||
public int CalculateRecyclePrice(int basePrice)
|
||||
{
|
||||
return Mathf.FloorToInt(basePrice * (GameEntry.Luban.Global.WeaponRecycleRate / 1000f));
|
||||
return Mathf.FloorToInt(basePrice * GameEntry.Luban.Global.WeaponRecycleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user