Browse Source

修改对战的伤害计算公式。

master
hanqin 1 week ago
parent
commit
23d1049cd5
  1. 2
      Build_God_Api/Build_God_Api/Dto/CharacterDto.cs
  2. 6
      Build_God_Api/Build_God_Api/Dto/MonsterDtos.cs
  3. 24
      Build_God_Api/Build_God_Api/Services/BattleService.cs
  4. 2
      Build_God_Api/Build_God_Api/Services/CharacterService.cs
  5. 28
      Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs
  6. 8
      Build_God_Api/Build_God_Api/Services/Game/MonsterAttributeCalculateService.cs
  7. 2
      Build_God_Api/Build_God_Api/Services/MonsterService.cs
  8. 3
      Build_God_Game/src/api/character.ts
  9. 2
      Build_God_Game/src/api/monster.ts
  10. 39
      Build_God_Game/src/views/BattleView.vue

2
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; }

6
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; }
/// <summary>基础暴击伤害加成百分比(当前为 0)</summary>
public decimal BaseCriticalDamage { get; set; }
/// <summary>装备等提供的暴击伤害加成百分比</summary>
public decimal BonusCriticalDamage { get; set; }
public List<MonsterRewardDto>? Rewards { get; set; }
public List<MonsterEquipmentDto>? Equipment { get; set; }
}

24
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<BattleRewardDto?> GrantReward(int characterId, MonsterReward reward)

2
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,

28
Build_God_Api/Build_God_Api/Services/Game/CombatAttributeFormula.cs

@ -7,6 +7,34 @@ namespace Build_God_Api.Services.Game
/// </summary>
public static class CombatAttributeFormula
{
/// <summary>
/// 装备/角色未提供「暴击伤害」加成(百分比)时,用于暴击倍率的默认加成:倍率 = 1 + 该值/100(即 50 表示 1.5 倍)。
/// </summary>
public const decimal DefaultCriticalDamageBonusPercent = 50m;
/// <summary>
/// 暴击伤害加成百分比:若属性总和大于 0 则用属性(通常为装备「暴击伤害-百分比」累加),否则用 <see cref="DefaultCriticalDamageBonusPercent"/>。
/// 最终暴击倍率 = 1 + 返回值/100。
/// </summary>
public static decimal ResolveCriticalDamageBonusPercent(decimal baseBonus, decimal equipmentBonus)
{
decimal sum = baseBonus + equipmentBonus;
return sum > 0 ? sum : DefaultCriticalDamageBonusPercent;
}
/// <summary>
/// 单次攻击基础伤害(暴击、波动前)。采用 attack²/(attack+defense),使攻击略低于防御时仍能造成可观伤害,避免长期只有 1 点保底。
/// </summary>
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;

8
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; }
/// <summary>基础暴击伤害加成百分比(当前怪物恒为 0)</summary>
public decimal BaseCriticalDamage { get; set; }
/// <summary>装备提供的暴击伤害加成百分比</summary>
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,

2
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
};

3
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

2
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[]
}

39
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,

Loading…
Cancel
Save