2020-07-23 19:03:58 -04:00
|
|
|
package diablo2stats
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
2020-09-20 17:52:01 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
|
|
|
|
|
2020-07-23 19:03:58 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2stats"
|
|
|
|
)
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
// static check that diablo2Stat implements Stat
|
|
|
|
var _ d2stats.Stat = &diablo2Stat{}
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
type descValPosition int
|
|
|
|
|
|
|
|
const (
|
|
|
|
descValHide descValPosition = iota
|
|
|
|
descValPrefix
|
|
|
|
descValPostfix
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
maxSkillTabIndex = 2
|
2020-07-23 22:12:48 -04:00
|
|
|
oneValue = 1
|
|
|
|
twoValue = 2
|
|
|
|
threeValue = 3
|
|
|
|
fourValue = 4
|
2020-07-23 19:03:58 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
twoComponentStr = "%s %s"
|
|
|
|
threeComponentStr = "%s %s %s"
|
|
|
|
fourComponentStr = "%s %s %s %s"
|
|
|
|
)
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
const (
|
|
|
|
intVal = d2stats.StatValueInt
|
|
|
|
sum = d2stats.StatValueCombineSum
|
|
|
|
static = d2stats.StatValueCombineStatic
|
|
|
|
)
|
|
|
|
|
|
|
|
// diablo2Stat is an implementation of an OpenDiablo2 Stat, with a set of values.
|
|
|
|
// It is pretty tightly coupled to the data files for d2
|
|
|
|
type diablo2Stat struct {
|
2020-09-20 17:52:01 -04:00
|
|
|
factory *StatFactory
|
|
|
|
record *d2records.ItemStatCostRecord
|
|
|
|
values []d2stats.StatValue
|
2020-07-23 19:03:58 -04:00
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
// depending on the stat record, sets up the proper number of values,
|
|
|
|
// as well as set up the stat value number types, value combination types, and
|
|
|
|
// the value stringer functions used
|
2020-10-26 05:04:50 -04:00
|
|
|
func (s *diablo2Stat) init(numbers ...float64) { //nolint:funlen,gocyclo // can't reduce
|
2020-07-23 22:12:48 -04:00
|
|
|
if s.record == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-26 05:04:50 -04:00
|
|
|
//nolint:gomnd // introducing a const for these would be worse
|
2020-07-23 22:12:48 -04:00
|
|
|
switch s.record.DescFnID {
|
2020-07-30 10:14:15 -04:00
|
|
|
case 0:
|
|
|
|
// special case for poisonlength, or other stats, which have a
|
|
|
|
// 0-value descfnID field but need to store values
|
|
|
|
s.values = make([]d2stats.StatValue, len(numbers))
|
|
|
|
for idx := range s.values {
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[idx] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
2020-07-30 10:14:15 -04:00
|
|
|
}
|
2020-07-23 22:12:48 -04:00
|
|
|
case 1:
|
|
|
|
// +31 to Strength
|
|
|
|
// Replenish Life +20 || Drain Life -8
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 2:
|
|
|
|
// +16% Increased Chance of Blocking
|
|
|
|
// Lightning Absorb +10%
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 3:
|
|
|
|
// Damage Reduced by 25
|
|
|
|
// Slain Monsters Rest in Peace
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 4:
|
|
|
|
// Poison Resist +25%
|
|
|
|
// +25% Faster Run/Walk
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 5:
|
|
|
|
// Hit Causes Monster to Flee 25%
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
|
|
s.values[0].SetStringer(s.factory.stringerIntPercentageUnsigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 6:
|
|
|
|
// +25 to Life (Based on Character Level)
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 7:
|
|
|
|
// Lightning Resist +25% (Based on Character Level)
|
|
|
|
// +25% Better Chance of Getting Magic Items (Based on Character Level)
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 8:
|
|
|
|
// +25% Enhanced Defense (Based on Character Level)
|
|
|
|
// Heal Stamina Plus +25% (Based on Character Level)
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 9:
|
|
|
|
// Attacker Takes Damage of 25 (Based on Character Level)
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 11:
|
|
|
|
// Repairs 2 durability per second
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 12:
|
|
|
|
// Hit Blinds Target +5
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 13:
|
|
|
|
// +5 to Paladin Skill Levels
|
|
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerClassAllSkills)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 14:
|
|
|
|
// +5 to Combat Skills (Paladin Only)
|
|
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerClassOnly)
|
|
|
|
s.values[2] = s.factory.NewValue(intVal, static)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 15:
|
|
|
|
// 5% Chance to cast level 7 Frozen Orb on attack
|
|
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static)
|
|
|
|
s.values[2] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 16:
|
|
|
|
// Level 3 Warmth Aura When Equipped
|
|
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 20:
|
|
|
|
// -25% Target Defense
|
|
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 22:
|
|
|
|
// 25% to Attack Rating versus Specter
|
|
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageUnsigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerMonsterName)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 23:
|
|
|
|
// 25% Reanimate as: Specter
|
|
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
|
|
sum).SetStringer(s.factory.stringerIntPercentageUnsigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerMonsterName)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 24:
|
|
|
|
// Level 25 Frozen Orb (19/20 Charges)
|
|
|
|
s.values = make([]d2stats.StatValue, fourValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, static)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
|
|
|
s.values[2] = s.factory.NewValue(intVal, static)
|
|
|
|
s.values[3] = s.factory.NewValue(intVal, static)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 27:
|
|
|
|
// +25 to Frozen Orb (Paladin Only)
|
|
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
|
|
|
s.values[2] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerClassOnly)
|
2020-07-23 22:12:48 -04:00
|
|
|
case 28:
|
|
|
|
// +25 to Frozen Orb
|
|
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
2020-07-23 22:12:48 -04:00
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx := range numbers {
|
2020-07-30 10:14:15 -04:00
|
|
|
if idx > len(s.values)-1 {
|
2020-07-23 22:12:48 -04:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
s.values[idx].SetFloat(numbers[idx])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 19:03:58 -04:00
|
|
|
// Name returns the name of the stat (the key in itemstatcosts)
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) Name() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
return s.record.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Priority returns the description printing priority
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) Priority() int {
|
2020-07-23 19:03:58 -04:00
|
|
|
return s.record.DescPriority
|
|
|
|
}
|
|
|
|
|
|
|
|
// Values returns the stat values of the stat
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) Values() []d2stats.StatValue {
|
2020-07-23 19:03:58 -04:00
|
|
|
return s.values
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetValues sets the stat values
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) SetValues(values ...d2stats.StatValue) {
|
2020-07-23 19:03:58 -04:00
|
|
|
s.values = make([]d2stats.StatValue, len(values))
|
|
|
|
for idx := range values {
|
|
|
|
s.values[idx] = values[idx]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
// Clone returns a deep copy of the diablo2Stat
|
|
|
|
func (s *diablo2Stat) Clone() d2stats.Stat {
|
|
|
|
clone := &diablo2Stat{
|
2020-11-03 14:10:11 -05:00
|
|
|
factory: s.factory,
|
|
|
|
record: s.record,
|
2020-07-23 19:03:58 -04:00
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
clone.init()
|
|
|
|
|
2020-07-23 19:03:58 -04:00
|
|
|
for idx := range s.values {
|
|
|
|
srcVal := s.values[idx]
|
2020-07-23 22:12:48 -04:00
|
|
|
dstVal := &Diablo2StatValue{
|
|
|
|
numberType: srcVal.NumberType(),
|
|
|
|
combineType: srcVal.CombineType(),
|
|
|
|
}
|
|
|
|
|
|
|
|
dstVal.SetStringer(srcVal.Stringer())
|
2020-07-23 19:03:58 -04:00
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
switch srcVal.NumberType() {
|
2020-07-23 19:03:58 -04:00
|
|
|
case d2stats.StatValueInt:
|
|
|
|
dstVal.SetInt(srcVal.Int())
|
|
|
|
case d2stats.StatValueFloat:
|
|
|
|
dstVal.SetFloat(srcVal.Float())
|
|
|
|
}
|
|
|
|
|
2020-07-30 10:14:15 -04:00
|
|
|
if len(clone.values) < len(s.values) {
|
|
|
|
clone.values = make([]d2stats.StatValue, len(s.values))
|
|
|
|
}
|
|
|
|
|
2020-07-23 19:03:58 -04:00
|
|
|
clone.values[idx] = dstVal
|
|
|
|
}
|
|
|
|
|
|
|
|
return clone
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy to this stat value the values of the given stat value
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) Copy(from d2stats.Stat) d2stats.Stat {
|
2020-07-23 19:03:58 -04:00
|
|
|
srcValues := from.Values()
|
|
|
|
s.values = make([]d2stats.StatValue, len(srcValues))
|
|
|
|
|
|
|
|
for idx := range srcValues {
|
|
|
|
src := srcValues[idx]
|
2020-07-23 22:12:48 -04:00
|
|
|
valType := src.NumberType()
|
|
|
|
dst := &Diablo2StatValue{numberType: valType}
|
2020-07-23 19:03:58 -04:00
|
|
|
dst.SetStringer(src.Stringer())
|
|
|
|
|
|
|
|
switch valType {
|
|
|
|
case d2stats.StatValueInt:
|
|
|
|
dst.SetInt(src.Int())
|
|
|
|
case d2stats.StatValueFloat:
|
|
|
|
dst.SetFloat(src.Float())
|
|
|
|
}
|
|
|
|
|
|
|
|
s.values[idx] = dst
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine sums the other stat with this one (does not alter this stat, returns altered clone!)
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) Combine(other d2stats.Stat) (result d2stats.Stat, err error) {
|
2020-07-23 19:03:58 -04:00
|
|
|
cantBeCombinedErr := fmt.Errorf("cannot combine %s with %s", s.Name(), other.Name())
|
|
|
|
|
|
|
|
if !s.canBeCombinedWith(other) {
|
|
|
|
return nil, cantBeCombinedErr
|
|
|
|
}
|
|
|
|
|
|
|
|
result = s.Clone()
|
|
|
|
srcValues, dstValues := other.Values(), result.Values()
|
|
|
|
|
|
|
|
for idx := range result.Values() {
|
|
|
|
v1, v2 := dstValues[idx], srcValues[idx]
|
2020-07-23 22:12:48 -04:00
|
|
|
combinationRule := v1.CombineType()
|
|
|
|
|
|
|
|
if combinationRule == d2stats.StatValueCombineStatic {
|
|
|
|
// we do not add the values, they remain the same
|
|
|
|
// for things like monster/class/skill index or on proc stats
|
|
|
|
// where the level of a skill isn't summed, but the
|
|
|
|
// chance to cast values are
|
|
|
|
continue
|
|
|
|
}
|
2020-07-23 19:03:58 -04:00
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
if combinationRule == d2stats.StatValueCombineSum {
|
|
|
|
valType := v1.NumberType()
|
|
|
|
switch valType {
|
|
|
|
case d2stats.StatValueInt:
|
|
|
|
v1.SetInt(v1.Int() + v2.Int())
|
|
|
|
case d2stats.StatValueFloat:
|
|
|
|
v1.SetFloat(v1.Float() + v2.Float())
|
|
|
|
}
|
2020-07-23 19:03:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) canBeCombinedWith(other d2stats.Stat) bool {
|
2020-07-23 19:03:58 -04:00
|
|
|
if s.Name() != other.Name() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
values1, values2 := s.Values(), other.Values()
|
|
|
|
if len(values1) != len(values2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx := range values1 {
|
2020-07-23 22:12:48 -04:00
|
|
|
if values1[idx].NumberType() != values2[idx].NumberType() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if values1[idx].CombineType() != values2[idx].CombineType() {
|
2020-07-23 19:03:58 -04:00
|
|
|
return false
|
|
|
|
}
|
2020-07-23 22:12:48 -04:00
|
|
|
|
|
|
|
// in the case that we are trying to combine stats like:
|
|
|
|
// +1 to Paladin Skills
|
|
|
|
// +1 to Sorceress Skills
|
|
|
|
// the numeric value (an index) that denotes the class skill type knows not to be summed
|
|
|
|
// with the other index, even though the format of the stat and stat value is pretty much
|
|
|
|
// the same.
|
|
|
|
if values1[idx].CombineType() == d2stats.StatValueCombineStatic {
|
|
|
|
if values1[idx].Float() != values2[idx].Float() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-07-23 19:03:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns the formatted description string
|
2020-10-26 05:04:50 -04:00
|
|
|
func (s *diablo2Stat) String() string { //nolint:gocyclo // switch statement is not so bad
|
2020-07-23 19:03:58 -04:00
|
|
|
var result string
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
for idx := range s.values {
|
|
|
|
if s.values[idx].Stringer() == nil {
|
2020-09-20 17:52:01 -04:00
|
|
|
s.values[idx].SetStringer(s.factory.stringerUnsignedInt)
|
2020-07-23 22:12:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 05:04:50 -04:00
|
|
|
//nolint:gomnd // introducing a const for these would be worse
|
2020-07-23 19:03:58 -04:00
|
|
|
switch s.record.DescFnID {
|
2020-07-23 22:12:48 -04:00
|
|
|
case 1, 2, 3, 4, 5, 12, 20:
|
2020-07-23 19:03:58 -04:00
|
|
|
result = s.descFn1()
|
2020-07-23 22:12:48 -04:00
|
|
|
case 6, 7, 8:
|
2020-07-23 19:03:58 -04:00
|
|
|
result = s.descFn6()
|
|
|
|
case 9:
|
|
|
|
result = s.descFn9()
|
|
|
|
case 11:
|
|
|
|
result = s.descFn11()
|
|
|
|
case 13:
|
|
|
|
result = s.descFn13()
|
|
|
|
case 14:
|
|
|
|
result = s.descFn14()
|
|
|
|
case 15:
|
|
|
|
result = s.descFn15()
|
|
|
|
case 16:
|
|
|
|
result = s.descFn16()
|
2020-07-23 22:12:48 -04:00
|
|
|
case 22, 23:
|
2020-07-23 19:03:58 -04:00
|
|
|
result = s.descFn22()
|
|
|
|
case 24:
|
|
|
|
result = s.descFn24()
|
|
|
|
case 27:
|
|
|
|
result = s.descFn27()
|
|
|
|
case 28:
|
|
|
|
result = s.descFn28()
|
|
|
|
default:
|
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn1() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
var stringTableKey, result string
|
|
|
|
|
|
|
|
value := s.values[0]
|
|
|
|
|
|
|
|
formatString := twoComponentStr
|
|
|
|
|
|
|
|
if value.Int() < 0 {
|
|
|
|
stringTableKey = s.record.DescStrNeg
|
|
|
|
} else {
|
|
|
|
stringTableKey = s.record.DescStrPos
|
|
|
|
}
|
|
|
|
|
2020-11-03 14:10:11 -05:00
|
|
|
stringTableString := s.factory.asset.TranslateString(stringTableKey)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
switch descValPosition(s.record.DescVal) {
|
|
|
|
case descValPrefix:
|
|
|
|
result = fmt.Sprintf(formatString, value, stringTableString)
|
|
|
|
case descValPostfix:
|
|
|
|
result = fmt.Sprintf(formatString, stringTableString, value)
|
|
|
|
case descValHide:
|
|
|
|
result = stringTableString
|
|
|
|
default:
|
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn6() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
var stringTableKey, result string
|
|
|
|
|
|
|
|
value := s.values[0]
|
|
|
|
|
|
|
|
formatString := threeComponentStr
|
|
|
|
|
|
|
|
if value.Int() < 0 {
|
|
|
|
stringTableKey = s.record.DescStrNeg
|
|
|
|
} else {
|
|
|
|
stringTableKey = s.record.DescStrPos
|
|
|
|
}
|
|
|
|
|
2020-11-03 14:10:11 -05:00
|
|
|
str1 := s.factory.asset.TranslateString(stringTableKey)
|
|
|
|
str2 := s.factory.asset.TranslateString(s.record.DescStr2)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
switch descValPosition(s.record.DescVal) {
|
|
|
|
case descValPrefix:
|
|
|
|
result = fmt.Sprintf(formatString, value, str1, str2)
|
|
|
|
case descValPostfix:
|
|
|
|
result = fmt.Sprintf(formatString, str1, value, str2)
|
|
|
|
case descValHide:
|
|
|
|
formatString = twoComponentStr
|
|
|
|
result = fmt.Sprintf(formatString, value, str2)
|
|
|
|
default:
|
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn9() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
var stringTableKey, result string
|
|
|
|
|
|
|
|
value := s.values[0]
|
|
|
|
|
|
|
|
formatString := threeComponentStr
|
|
|
|
|
|
|
|
if value.Int() < 0 {
|
|
|
|
stringTableKey = s.record.DescStrNeg
|
|
|
|
} else {
|
|
|
|
stringTableKey = s.record.DescStrPos
|
|
|
|
}
|
|
|
|
|
2020-11-03 14:10:11 -05:00
|
|
|
str1 := s.factory.asset.TranslateString(stringTableKey)
|
|
|
|
str2 := s.factory.asset.TranslateString(s.record.DescStr2)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
switch descValPosition(s.record.DescVal) {
|
|
|
|
case descValPrefix:
|
|
|
|
result = fmt.Sprintf(formatString, value, str1, str2)
|
|
|
|
case descValPostfix:
|
|
|
|
result = fmt.Sprintf(formatString, str1, value, str2)
|
|
|
|
case descValHide:
|
|
|
|
result = fmt.Sprintf(twoComponentStr, value, str2)
|
|
|
|
default:
|
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn11() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
var stringTableKey string
|
|
|
|
|
|
|
|
value := s.values[0]
|
|
|
|
|
|
|
|
if value.Int() < 0 {
|
|
|
|
stringTableKey = s.record.DescStrNeg
|
|
|
|
} else {
|
|
|
|
stringTableKey = s.record.DescStrPos
|
|
|
|
}
|
|
|
|
|
2020-11-03 14:10:11 -05:00
|
|
|
str1 := s.factory.asset.TranslateString(stringTableKey)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
formatString := str1
|
|
|
|
|
|
|
|
return fmt.Sprintf(formatString, value)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn13() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
value := s.values[0]
|
|
|
|
allSkills := s.values[1]
|
|
|
|
|
|
|
|
formatString := twoComponentStr
|
|
|
|
|
|
|
|
switch descValPosition(s.record.DescVal) {
|
|
|
|
case descValPrefix:
|
|
|
|
return fmt.Sprintf(formatString, value, allSkills)
|
|
|
|
case descValPostfix:
|
|
|
|
return fmt.Sprintf(formatString, allSkills, value)
|
|
|
|
case descValHide:
|
|
|
|
return allSkills.String()
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn14() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
// strings come out like `+5 to Combat Skills (Paladin Only)`
|
|
|
|
numSkills, hero, skillTab := s.values[0], s.values[1], s.values[2]
|
2020-09-20 17:52:01 -04:00
|
|
|
heroMap := s.factory.getHeroMap()
|
2020-07-23 19:03:58 -04:00
|
|
|
heroIndex := hero.Int()
|
2020-09-20 17:52:01 -04:00
|
|
|
classRecord := s.factory.asset.Records.Character.Stats[heroMap[heroIndex]]
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
// diablo 2 is hardcoded to have only 3 skill tabs
|
|
|
|
skillTabIndex := skillTab.Int()
|
|
|
|
if skillTabIndex < 0 || skillTabIndex > maxSkillTabIndex {
|
|
|
|
skillTabIndex = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// `+5`
|
|
|
|
numSkillsStr := numSkills.String()
|
|
|
|
|
|
|
|
// `to Combat Skills`
|
|
|
|
skillTabKey := classRecord.SkillStrTab[skillTabIndex]
|
2020-11-03 14:10:11 -05:00
|
|
|
skillTabStr := s.factory.asset.TranslateString(skillTabKey)
|
2020-07-23 19:03:58 -04:00
|
|
|
skillTabStr = strings.ReplaceAll(skillTabStr, "+%d ", "") // has a token we dont need
|
|
|
|
|
|
|
|
// `(Paladin Only)`
|
|
|
|
classOnlyStr := hero.String()
|
|
|
|
|
|
|
|
return fmt.Sprintf(threeComponentStr, numSkillsStr, skillTabStr, classOnlyStr)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn15() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
chance, lvl, skill := s.values[0], s.values[1], s.values[2]
|
|
|
|
|
|
|
|
// Special case, `chance to cast` format is actually in the string table!
|
2020-11-03 14:10:11 -05:00
|
|
|
chanceToCastStr := s.factory.asset.TranslateString(s.record.DescStrPos)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
return fmt.Sprintf(chanceToCastStr, chance.Int(), lvl.Int(), skill)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn16() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
skillLevel, skillIndex := s.values[0], s.values[1]
|
|
|
|
|
|
|
|
// Special case, `Level # XYZ Aura When Equipped`, format is actually in the string table!
|
2020-11-03 14:10:11 -05:00
|
|
|
format := s.factory.asset.TranslateString(s.record.DescStrPos)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
return fmt.Sprintf(format, skillLevel.Int(), skillIndex)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn22() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
arBonus, monsterIndex := s.values[0], s.values[1]
|
2020-11-03 14:10:11 -05:00
|
|
|
arVersus := s.factory.asset.TranslateString(s.record.DescStrPos)
|
2020-07-23 19:03:58 -04:00
|
|
|
|
|
|
|
return fmt.Sprintf(threeComponentStr, arBonus, arVersus, monsterIndex)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn24() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
// Special case formatting
|
|
|
|
format := "Level " + threeComponentStr
|
|
|
|
|
|
|
|
lvl, skill, chargeMax, chargeCurrent := s.values[0],
|
|
|
|
s.values[1],
|
|
|
|
s.values[2].Int(),
|
|
|
|
s.values[3].Int()
|
|
|
|
|
2020-11-03 14:10:11 -05:00
|
|
|
chargeStr := s.factory.asset.TranslateString(s.record.DescStrPos)
|
2020-07-23 19:03:58 -04:00
|
|
|
chargeStr = fmt.Sprintf(chargeStr, chargeCurrent, chargeMax)
|
|
|
|
|
|
|
|
return fmt.Sprintf(format, lvl, skill, chargeStr)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn27() string {
|
2020-07-30 10:14:15 -04:00
|
|
|
// property "skill-rand" will try to make an instance with an invalid hero index
|
|
|
|
// in this case, we use descfn 28
|
|
|
|
if s.values[2].Int() == -1 {
|
|
|
|
return s.descFn28()
|
|
|
|
}
|
|
|
|
|
2020-07-23 19:03:58 -04:00
|
|
|
amount, skill, hero := s.values[0], s.values[1], s.values[2]
|
|
|
|
|
|
|
|
return fmt.Sprintf(fourComponentStr, amount, "to", skill, hero)
|
|
|
|
}
|
|
|
|
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) descFn28() string {
|
2020-07-23 19:03:58 -04:00
|
|
|
amount, skill := s.values[0], s.values[1]
|
|
|
|
|
|
|
|
return fmt.Sprintf(threeComponentStr, amount, "to", skill)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DescGroupString return a string based on the DescGroupFuncID
|
2020-07-23 22:12:48 -04:00
|
|
|
func (s *diablo2Stat) DescGroupString(a ...interface{}) string {
|
2020-07-23 19:03:58 -04:00
|
|
|
if s.record.DescGroupFuncID < 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
format := ""
|
|
|
|
for range a {
|
|
|
|
format += "%s "
|
|
|
|
}
|
|
|
|
|
|
|
|
format = strings.Trim(format, " ")
|
|
|
|
|
|
|
|
return fmt.Sprintf(format, a...)
|
|
|
|
}
|