SimulateCombat runs an instant fight between two mobs identified by their template IDs. levelA/levelB override the template level when > 0. maxRounds caps the fight length (defaults to 100 if <= 0).
(mobIdA, mobIdB mobs.MobId, levelA, levelB int, maxRounds int)
| 95 | // template IDs. levelA/levelB override the template level when > 0. |
| 96 | // maxRounds caps the fight length (defaults to 100 if <= 0). |
| 97 | func SimulateCombat(mobIdA, mobIdB mobs.MobId, levelA, levelB int, maxRounds int) (SimResult, error) { |
| 98 | if maxRounds <= 0 { |
| 99 | maxRounds = 100 |
| 100 | } |
| 101 | |
| 102 | mobA, err := newSimMob(mobIdA, levelA) |
| 103 | if err != nil { |
| 104 | return SimResult{}, fmt.Errorf("combatant A: %w", err) |
| 105 | } |
| 106 | mobB, err := newSimMob(mobIdB, levelB) |
| 107 | if err != nil { |
| 108 | return SimResult{}, fmt.Errorf("combatant B: %w", err) |
| 109 | } |
| 110 | |
| 111 | charA := &mobA.Character |
| 112 | charB := &mobB.Character |
| 113 | |
| 114 | charA.SetAggro(0, mobB.InstanceId, characters.DefaultAttack) |
| 115 | charB.SetAggro(0, mobA.InstanceId, characters.DefaultAttack) |
| 116 | |
| 117 | charA.CancelBuffsWithFlag(buffs.CancelIfCombat) |
| 118 | charB.CancelBuffsWithFlag(buffs.CancelIfCombat) |
| 119 | |
| 120 | result := SimResult{ |
| 121 | NameA: charA.Name, |
| 122 | NameB: charB.Name, |
| 123 | LevelA: charA.Level, |
| 124 | LevelB: charB.Level, |
| 125 | } |
| 126 | |
| 127 | for round := 1; round <= maxRounds; round++ { |
| 128 | roundDmgA, roundDmgB := 0, 0 |
| 129 | |
| 130 | // A attacks B |
| 131 | atkResult := calculateCombat(*charA, *charB, Mob, Mob) |
| 132 | charB.ApplyHealthChange(atkResult.DamageToTarget * -1) |
| 133 | charA.ApplyHealthChange(atkResult.DamageToSource * -1) |
| 134 | result.DamageByA += atkResult.DamageToTarget |
| 135 | roundDmgA = atkResult.DamageToTarget |
| 136 | applySimBuffs(charA, atkResult.BuffSource) |
| 137 | applySimBuffs(charB, atkResult.BuffTarget) |
| 138 | |
| 139 | if charB.Health <= 0 { |
| 140 | result.Winner = charA.Name |
| 141 | result.WinnerSide = 1 |
| 142 | result.Rounds = round |
| 143 | result.HealthRemainingA = charA.Health |
| 144 | result.HealthRemainingB = charB.Health |
| 145 | result.Log = append(result.Log, fmt.Sprintf( |
| 146 | "Round %d: %s deals %d → %s falls (%d hp)", |
| 147 | round, charA.Name, roundDmgA, charB.Name, charB.Health)) |
| 148 | return result, nil |
| 149 | } |
| 150 | |
| 151 | // B attacks A |
| 152 | defResult := calculateCombat(*charB, *charA, Mob, Mob) |
| 153 | charA.ApplyHealthChange(defResult.DamageToTarget * -1) |
| 154 | charB.ApplyHealthChange(defResult.DamageToSource * -1) |
nothing calls this directly
no test coverage detected