diff --git a/Build_God_Api/Build_God_Api/Dto/CharacterDto.cs b/Build_God_Api/Build_God_Api/Dto/CharacterDto.cs
index 12f3c6d..998e590 100644
--- a/Build_God_Api/Build_God_Api/Dto/CharacterDto.cs
+++ b/Build_God_Api/Build_God_Api/Dto/CharacterDto.cs
@@ -19,6 +19,8 @@
public decimal CriticalRate { get; set; }
public decimal BaseCriticalRate { get; set; }
public decimal BonusCriticalRate { get; set; }
+ public decimal BaseCriticalDamage { get; set; }
+ public decimal BonusCriticalDamage { get; set; }
public string ProfessionName { get; set; }
public decimal Money { get; set; }
public decimal CurrentExp { get; set; }
diff --git a/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs b/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs
index 9bf28ed..d6b6b45 100644
--- a/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs
+++ b/Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs
@@ -53,6 +53,12 @@ namespace Build_God_Api.Dto
public int Defense { get; set; }
public decimal CriticalRate { get; set; }
+ /// 基础暴击伤害加成百分比(当前为 0)
+ public decimal BaseCriticalDamage { get; set; }
+
+ /// 装备等提供的暴击伤害加成百分比
+ public decimal BonusCriticalDamage { get; set; }
+
public List? Rewards { get; set; }
public List? Equipment { get; set; }
}
diff --git a/Build_God_Api/Build_God_Api/Services/BattleService.cs b/Build_God_Api/Build_God_Api/Services/BattleService.cs
index f59d7cb..cda9981 100644
--- a/Build_God_Api/Build_God_Api/Services/BattleService.cs
+++ b/Build_God_Api/Build_God_Api/Services/BattleService.cs
@@ -72,12 +72,20 @@ namespace Build_God_Api.Services
{
round++;
- var playerDamage = CalculateDamage(playerAttack, monsterDefend, playerCriticalRate);
+ var playerDamage = CalculateDamage(
+ playerAttack,
+ monsterDefend,
+ playerCriticalRate,
+ CombatAttributeFormula.ResolveCriticalDamageBonusPercent(attributes.BaseCriticalDamage, attributes.BonusCriticalDamage));
monsterHealth -= playerDamage;
if (monsterHealth <= 0) break;
- var monsterDamage = CalculateDamage(monsterAttack, playerDefend, monsterStats.CriticalRate);
+ var monsterDamage = CalculateDamage(
+ monsterAttack,
+ playerDefend,
+ monsterStats.CriticalRate,
+ CombatAttributeFormula.ResolveCriticalDamageBonusPercent(monsterStats.BaseCriticalDamage, monsterStats.BonusCriticalDamage));
playerHealth -= monsterDamage;
}
@@ -124,20 +132,22 @@ namespace Build_God_Api.Services
}
}
- private int CalculateDamage(int attack, int defend, decimal criticalRate)
+ private int CalculateDamage(int attack, int defend, decimal criticalRatePercent, decimal criticalDamageBonusPercent)
{
- var baseDamage = Math.Max(1, attack - defend);
+ var baseDamage = CombatAttributeFormula.CalculateBaseDamageBeforeCritical(attack, defend);
var random = Random.Shared.NextDouble();
- var isCritical = random < (double)criticalRate / 100;
+ var isCritical = random < (double)criticalRatePercent / 100;
+ var critMult = 1m + criticalDamageBonusPercent / 100m;
+ decimal working = baseDamage;
if (isCritical)
{
- baseDamage = (int)(baseDamage * 1.5);
+ working *= critMult;
}
var variance = Random.Shared.Next(90, 111) / 100m;
- return (int)(baseDamage * variance);
+ return Math.Max(1, (int)(working * variance));
}
private async Task GrantReward(int characterId, MonsterReward reward)
diff --git a/Build_God_Api/Build_God_Api/Services/CharacterService.cs b/Build_God_Api/Build_God_Api/Services/CharacterService.cs
index d24f66c..2a2011c 100644
--- a/Build_God_Api/Build_God_Api/Services/CharacterService.cs
+++ b/Build_God_Api/Build_God_Api/Services/CharacterService.cs
@@ -299,6 +299,8 @@ namespace Build_God_Api.Services
CriticalRate = attrs.CriticalRate,
BaseCriticalRate = attrs.BaseCriticalRate,
BonusCriticalRate = attrs.BonusCriticalRate,
+ BaseCriticalDamage = attrs.BaseCriticalDamage,
+ BonusCriticalDamage = attrs.BonusCriticalDamage,
ProfessionName = profession?.Name,
Money = c.Money,
CurrentExp = c.CurrentExp,
diff --git a/Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs b/Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs
index 18d29f0..8fe7e7c 100644
--- a/Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs
+++ b/Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs
@@ -7,6 +7,34 @@ namespace Build_God_Api.Services.Game
///
public static class CombatAttributeFormula
{
+ ///
+ /// 装备/角色未提供「暴击伤害」加成(百分比)时,用于暴击倍率的默认加成:倍率 = 1 + 该值/100(即 50 表示 1.5 倍)。
+ ///
+ public const decimal DefaultCriticalDamageBonusPercent = 50m;
+
+ ///
+ /// 暴击伤害加成百分比:若属性总和大于 0 则用属性(通常为装备「暴击伤害-百分比」累加),否则用 。
+ /// 最终暴击倍率 = 1 + 返回值/100。
+ ///
+ public static decimal ResolveCriticalDamageBonusPercent(decimal baseBonus, decimal equipmentBonus)
+ {
+ decimal sum = baseBonus + equipmentBonus;
+ return sum > 0 ? sum : DefaultCriticalDamageBonusPercent;
+ }
+
+ ///
+ /// 单次攻击基础伤害(暴击、波动前)。采用 attack²/(attack+defense),使攻击略低于防御时仍能造成可观伤害,避免长期只有 1 点保底。
+ ///
+ public static int CalculateBaseDamageBeforeCritical(int attack, int defend)
+ {
+ long a = Math.Max(0, attack);
+ long d = Math.Max(0, defend);
+ if (a <= 0) return 1;
+ double raw = (double)a * a / (a + d);
+ int damage = (int)Math.Floor(raw);
+ return Math.Max(1, damage);
+ }
+
public static decimal CalculateBaseMaxHP(int levelId, decimal expForLog, Profession? profession)
{
decimal healthRate = profession?.HealthRate ?? 1m;
diff --git a/Build_God_Api/Build_God_Api/Services/Game/MonsterAttributeCalculateService.cs b/Build_God_Api/Build_God_Api/Services/Game/MonsterAttributeCalculateService.cs
index 73f6a34..a2aa051 100644
--- a/Build_God_Api/Build_God_Api/Services/Game/MonsterAttributeCalculateService.cs
+++ b/Build_God_Api/Build_God_Api/Services/Game/MonsterAttributeCalculateService.cs
@@ -22,6 +22,12 @@ namespace Build_God_Api.Services.Game
public decimal CriticalRate { get; set; }
public decimal BaseCriticalRate { get; set; }
public decimal BonusCriticalRate { get; set; }
+
+ /// 基础暴击伤害加成百分比(当前怪物恒为 0)
+ public decimal BaseCriticalDamage { get; set; }
+
+ /// 装备提供的暴击伤害加成百分比
+ public decimal BonusCriticalDamage { get; set; }
}
public class MonsterAttributeCalculateService(ISqlSugarClient db) : IMonsterAttributeCalculateService
@@ -52,6 +58,8 @@ namespace Build_God_Api.Services.Game
BonusAttack = bonus.AttackBonus,
BonusDefend = bonus.DefendBonus,
BonusCriticalRate = bonus.CriticalBonus,
+ BaseCriticalDamage = 0,
+ BonusCriticalDamage = bonus.CriticalDamageBonus,
MaxHP = baseMaxHp + bonus.HPBonus,
Attack = baseAttack + bonus.AttackBonus,
Defend = baseDefend + bonus.DefendBonus,
diff --git a/Build_God_Api/Build_God_Api/Services/MonsterService.cs b/Build_God_Api/Build_God_Api/Services/MonsterService.cs
index 87219ae..fce6677 100644
--- a/Build_God_Api/Build_God_Api/Services/MonsterService.cs
+++ b/Build_God_Api/Build_God_Api/Services/MonsterService.cs
@@ -77,6 +77,8 @@ namespace Build_God_Api.Services
Attack = (int)stats.Attack,
Defense = (int)stats.Defend,
CriticalRate = stats.CriticalRate,
+ BaseCriticalDamage = stats.BaseCriticalDamage,
+ BonusCriticalDamage = stats.BonusCriticalDamage,
Rewards = rewards,
Equipment = equipDtos
};
diff --git a/Build_God_Game/src/api/character.ts b/Build_God_Game/src/api/character.ts
index f41dc12..ecbd4c3 100644
--- a/Build_God_Game/src/api/character.ts
+++ b/Build_God_Game/src/api/character.ts
@@ -17,6 +17,9 @@ export interface CharacterDto {
criticalRate: number
baseCriticalRate: number
bonusCriticalRate: number
+ /** 暴击伤害加成百分比(装备等);缺省时前端按 0 参与累加,战斗再套用默认 50 */
+ baseCriticalDamage?: number
+ bonusCriticalDamage?: number
professionName?: string
money: number
currentExp: number
diff --git a/Build_God_Game/src/api/monster.ts b/Build_God_Game/src/api/monster.ts
index a6abf8a..c4eb44f 100644
--- a/Build_God_Game/src/api/monster.ts
+++ b/Build_God_Game/src/api/monster.ts
@@ -22,6 +22,8 @@ export interface MonsterDto {
attack: number
defense: number
criticalRate: number
+ baseCriticalDamage?: number
+ bonusCriticalDamage?: number
rewards?: MonsterRewardDto[]
equipment?: MonsterEquipmentClientDto[]
}
diff --git a/Build_God_Game/src/views/BattleView.vue b/Build_God_Game/src/views/BattleView.vue
index 1bf2b53..a44875f 100644
--- a/Build_God_Game/src/views/BattleView.vue
+++ b/Build_God_Game/src/views/BattleView.vue
@@ -54,13 +54,32 @@ const getMonsterIcon = (iconName?: string) => {
return `/src/assets/images/monster/${iconName}`
}
-const calculateDamage = (attack: number, defend: number, criticalRate: number): { damage: number; isCritical: boolean } => {
- const baseDamage = Math.max(1, attack - defend)
+const defaultCriticalDamageBonusPercent = 50
+
+const resolveCriticalDamageBonusPercent = (baseBonus: number, equipmentBonus: number) => {
+ const sum = (baseBonus || 0) + (equipmentBonus || 0)
+ return sum > 0 ? sum : defaultCriticalDamageBonusPercent
+}
+
+/** 与后端 CombatAttributeFormula / BattleService 一致:基础伤害 attack²/(attack+defend),暴击倍率 1+暴击伤害%/100 */
+const calculateDamage = (
+ attack: number,
+ defend: number,
+ criticalRate: number,
+ criticalDamageBonusPercent: number
+): { damage: number; isCritical: boolean } => {
+ const a = Math.max(0, attack)
+ const d = Math.max(0, defend)
+ const baseDamage = a <= 0 ? 1 : Math.max(1, Math.floor((a * a) / (a + d)))
const random = Math.random()
const isCritical = random < criticalRate / 100
- const damage = isCritical ? Math.floor(baseDamage * 1.5) : baseDamage
+ const critMult = 1 + criticalDamageBonusPercent / 100
+ let working = baseDamage
+ if (isCritical) {
+ working = Math.floor(working * critMult)
+ }
const variance = (90 + Math.floor(Math.random() * 21)) / 100
- return { damage: Math.floor(damage * variance), isCritical }
+ return { damage: Math.max(1, Math.floor(working * variance)), isCritical }
}
const startBattle = async () => {
@@ -82,8 +101,16 @@ const startBattle = async () => {
const playerAttack = currentCharacter.value.attack
const playerDefend = currentCharacter.value.defend
const playerCriticalRate = currentCharacter.value.criticalRate
+ const playerCritDamagePct = resolveCriticalDamageBonusPercent(
+ currentCharacter.value.baseCriticalDamage,
+ currentCharacter.value.bonusCriticalDamage
+ )
const monsterAttack = currentMonster.value.attack
const monsterDefend = currentMonster.value.defense
+ const monsterCritDamagePct = resolveCriticalDamageBonusPercent(
+ currentMonster.value.baseCriticalDamage ?? 0,
+ currentMonster.value.bonusCriticalDamage ?? 0
+ )
let round = 0
const maxRounds = 100
@@ -94,7 +121,7 @@ const startBattle = async () => {
round++
// 玩家攻击
- const playerResult = calculateDamage(playerAttack, monsterDefend, playerCriticalRate)
+ const playerResult = calculateDamage(playerAttack, monsterDefend, playerCriticalRate, playerCritDamagePct)
monsterHealth.value = Math.max(0, monsterHealth.value - playerResult.damage)
battleLog.value.push({
round,
@@ -109,7 +136,7 @@ const startBattle = async () => {
}
// 怪物攻击
- const monsterResult = calculateDamage(monsterAttack, playerDefend, currentMonster.value.criticalRate)
+ const monsterResult = calculateDamage(monsterAttack, playerDefend, currentMonster.value.criticalRate, monsterCritDamagePct)
playerHealth.value = Math.max(0, playerHealth.value - monsterResult.damage)
battleLog.value.push({
round,