mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-18 02:16:23 -05:00
fc87b2be7a
* Remove weapons, armor, misc, itemCommon, itemTyps datadict singletons - removed loader calls from d2app - removed the HeroObjects singleton from `d2core/d2inventory` - added an InventoryItemFactory in d2inventory - package-level functions that use data records are now methods of the InventoryItemFactory - renamed ItemGenerator in d2item to ItemFactory - package-level functions that use records are now methods of ItemFactory - d2map.MapEntityFactory now has an item factory instance for creating items - fixed a bug in unique item record loader where it loaded an empty record - added a PlayerStateFactory for creating a player state (uses the asset manager) - updated the test inventory/equipment code in d2player to handle errors from the ItemFactory - character select and character creation screens have a player state and inventory item factory - updated item tests to use the item factory * minor edit * Removed d2datadict.Experience singleton added a HeroStatsFactory, much like the other factories. The factory gets an asset manager reference in order to use data records. * removed d2datadict.AutoMagic singleton * removed d2datadict.AutoMap singleton * removed d2datadict.BodyLocations singleton * removed d2datadict.Books singleton * Removed singletons for level records - removed loader calls in d2app - changed type references from d2datadict to d2records - added a `MapGenerator` in d2mapgen which uses thew asset manager and map engine - package-level map generation functions are now MapGenerator methods - `d2datadict.GetLevelDetails(id int)` is now a method of the RecordManager * remove SkillCalc and MissileCalc singletons * Removed CharStats and ItemStatCost singletons - added an ItemStatFactory which uses the asset manager to create stats - package-level functions for stats in d2item are now StatFactory methods - changed type references from d2datadict to d2records - `d2player.GetAllPlayerStates` is now a method of the `PlayerStateFactory` * Removed DkillDesc and Skills singletons from d2datadict - removed loader calls from d2app - diablo2stats.Stat instances are given a reference to the factory for doing record lookups * update the stats test to use mock a asset manager and stat factory * fixed diablo2stats tests and diablo2item tests * removed CompCodes singleton from d2datadict * remove cubemain singleton from d2datadict * removed DifficultyLevels singleton from d2datadict * removed ElemTypes singleton from d2datadict * removed events.go loader from d2datadict (was unused) * removed Gems singleton from d2datadict * removed Hireling and Inventory singletons from d2datadict * removed MagicPrefix and MagicSuffix singletons from d2datadict * removed ItemRatios singleton from d2datadict * removed Missiles singleton from d2datadict * removed MonModes singleton * Removed all monster and npc singletons from d2datadict - MapStamp instances now get a reference to their factory for doing record lookups * removed SoundEntry and SoundEnviron singletons from d2datadict
616 lines
17 KiB
Go
616 lines
17 KiB
Go
package diablo2stats
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2stats"
|
|
)
|
|
|
|
// static check that diablo2Stat implements Stat
|
|
var _ d2stats.Stat = &diablo2Stat{}
|
|
|
|
type descValPosition int
|
|
|
|
const (
|
|
descValHide descValPosition = iota
|
|
descValPrefix
|
|
descValPostfix
|
|
)
|
|
|
|
const (
|
|
maxSkillTabIndex = 2
|
|
oneValue = 1
|
|
twoValue = 2
|
|
threeValue = 3
|
|
fourValue = 4
|
|
)
|
|
|
|
const (
|
|
twoComponentStr = "%s %s"
|
|
threeComponentStr = "%s %s %s"
|
|
fourComponentStr = "%s %s %s %s"
|
|
)
|
|
|
|
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 {
|
|
factory *StatFactory
|
|
record *d2records.ItemStatCostRecord
|
|
values []d2stats.StatValue
|
|
}
|
|
|
|
// 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
|
|
func (s *diablo2Stat) init(numbers ...float64) { //nolint:funlen doesn't make sense to split
|
|
if s.record == nil {
|
|
return
|
|
}
|
|
|
|
//nolint:gomdn introducing a const for these would be worse
|
|
switch s.record.DescFnID {
|
|
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 {
|
|
s.values[idx] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
}
|
|
case 1:
|
|
// +31 to Strength
|
|
// Replenish Life +20 || Drain Life -8
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
case 2:
|
|
// +16% Increased Chance of Blocking
|
|
// Lightning Absorb +10%
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
|
case 3:
|
|
// Damage Reduced by 25
|
|
// Slain Monsters Rest in Peace
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
case 4:
|
|
// Poison Resist +25%
|
|
// +25% Faster Run/Walk
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
|
case 5:
|
|
// Hit Causes Monster to Flee 25%
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
s.values[0].SetStringer(s.factory.stringerIntPercentageUnsigned)
|
|
case 6:
|
|
// +25 to Life (Based on Character Level)
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
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)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
|
case 8:
|
|
// +25% Enhanced Defense (Based on Character Level)
|
|
// Heal Stamina Plus +25% (Based on Character Level)
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
|
case 9:
|
|
// Attacker Takes Damage of 25 (Based on Character Level)
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
case 11:
|
|
// Repairs 2 durability per second
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
case 12:
|
|
// Hit Blinds Target +5
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
case 13:
|
|
// +5 to Paladin Skill Levels
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
s.values[1] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerClassAllSkills)
|
|
case 14:
|
|
// +5 to Combat Skills (Paladin Only)
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
|
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)
|
|
case 15:
|
|
// 5% Chance to cast level 7 Frozen Orb on attack
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
|
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)
|
|
case 16:
|
|
// Level 3 Warmth Aura When Equipped
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum)
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
|
case 20:
|
|
// -25% Target Defense
|
|
s.values = make([]d2stats.StatValue, oneValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageSigned)
|
|
case 22:
|
|
// 25% to Attack Rating versus Specter
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageUnsigned)
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerMonsterName)
|
|
case 23:
|
|
// 25% Reanimate as: Specter
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
|
s.values[0] = s.factory.NewValue(intVal,
|
|
sum).SetStringer(s.factory.stringerIntPercentageUnsigned)
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerMonsterName)
|
|
case 24:
|
|
// Level 25 Frozen Orb (19/20 Charges)
|
|
s.values = make([]d2stats.StatValue, fourValue)
|
|
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)
|
|
case 27:
|
|
// +25 to Frozen Orb (Paladin Only)
|
|
s.values = make([]d2stats.StatValue, threeValue)
|
|
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)
|
|
case 28:
|
|
// +25 to Frozen Orb
|
|
s.values = make([]d2stats.StatValue, twoValue)
|
|
s.values[0] = s.factory.NewValue(intVal, sum).SetStringer(s.factory.stringerIntSigned)
|
|
s.values[1] = s.factory.NewValue(intVal, static).SetStringer(s.factory.stringerSkillName)
|
|
default:
|
|
return
|
|
}
|
|
|
|
for idx := range numbers {
|
|
if idx > len(s.values)-1 {
|
|
break
|
|
}
|
|
|
|
s.values[idx].SetFloat(numbers[idx])
|
|
}
|
|
}
|
|
|
|
// Name returns the name of the stat (the key in itemstatcosts)
|
|
func (s *diablo2Stat) Name() string {
|
|
return s.record.Name
|
|
}
|
|
|
|
// Priority returns the description printing priority
|
|
func (s *diablo2Stat) Priority() int {
|
|
return s.record.DescPriority
|
|
}
|
|
|
|
// Values returns the stat values of the stat
|
|
func (s *diablo2Stat) Values() []d2stats.StatValue {
|
|
return s.values
|
|
}
|
|
|
|
// SetValues sets the stat values
|
|
func (s *diablo2Stat) SetValues(values ...d2stats.StatValue) {
|
|
s.values = make([]d2stats.StatValue, len(values))
|
|
for idx := range values {
|
|
s.values[idx] = values[idx]
|
|
}
|
|
}
|
|
|
|
// Clone returns a deep copy of the diablo2Stat
|
|
func (s *diablo2Stat) Clone() d2stats.Stat {
|
|
clone := &diablo2Stat{
|
|
record: s.record,
|
|
}
|
|
|
|
clone.init()
|
|
|
|
for idx := range s.values {
|
|
srcVal := s.values[idx]
|
|
dstVal := &Diablo2StatValue{
|
|
numberType: srcVal.NumberType(),
|
|
combineType: srcVal.CombineType(),
|
|
}
|
|
|
|
dstVal.SetStringer(srcVal.Stringer())
|
|
|
|
switch srcVal.NumberType() {
|
|
case d2stats.StatValueInt:
|
|
dstVal.SetInt(srcVal.Int())
|
|
case d2stats.StatValueFloat:
|
|
dstVal.SetFloat(srcVal.Float())
|
|
}
|
|
|
|
if len(clone.values) < len(s.values) {
|
|
clone.values = make([]d2stats.StatValue, len(s.values))
|
|
}
|
|
|
|
clone.values[idx] = dstVal
|
|
}
|
|
|
|
return clone
|
|
}
|
|
|
|
// Copy to this stat value the values of the given stat value
|
|
func (s *diablo2Stat) Copy(from d2stats.Stat) d2stats.Stat {
|
|
srcValues := from.Values()
|
|
s.values = make([]d2stats.StatValue, len(srcValues))
|
|
|
|
for idx := range srcValues {
|
|
src := srcValues[idx]
|
|
valType := src.NumberType()
|
|
dst := &Diablo2StatValue{numberType: valType}
|
|
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!)
|
|
func (s *diablo2Stat) Combine(other d2stats.Stat) (result d2stats.Stat, err error) {
|
|
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]
|
|
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
|
|
}
|
|
|
|
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())
|
|
}
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (s *diablo2Stat) canBeCombinedWith(other d2stats.Stat) bool {
|
|
if s.Name() != other.Name() {
|
|
return false
|
|
}
|
|
|
|
values1, values2 := s.Values(), other.Values()
|
|
if len(values1) != len(values2) {
|
|
return false
|
|
}
|
|
|
|
for idx := range values1 {
|
|
if values1[idx].NumberType() != values2[idx].NumberType() {
|
|
return false
|
|
}
|
|
|
|
if values1[idx].CombineType() != values2[idx].CombineType() {
|
|
return false
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// String returns the formatted description string
|
|
func (s *diablo2Stat) String() string { //nolint:gocyclo switch statement is not so bad
|
|
var result string
|
|
|
|
for idx := range s.values {
|
|
if s.values[idx].Stringer() == nil {
|
|
s.values[idx].SetStringer(s.factory.stringerUnsignedInt)
|
|
}
|
|
}
|
|
|
|
//nolint:gomdn introducing a const for these would be worse
|
|
switch s.record.DescFnID {
|
|
case 1, 2, 3, 4, 5, 12, 20:
|
|
result = s.descFn1()
|
|
case 6, 7, 8:
|
|
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()
|
|
case 22, 23:
|
|
result = s.descFn22()
|
|
case 24:
|
|
result = s.descFn24()
|
|
case 27:
|
|
result = s.descFn27()
|
|
case 28:
|
|
result = s.descFn28()
|
|
default:
|
|
result = ""
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn1() string {
|
|
var stringTableKey, result string
|
|
|
|
value := s.values[0]
|
|
|
|
formatString := twoComponentStr
|
|
|
|
if value.Int() < 0 {
|
|
stringTableKey = s.record.DescStrNeg
|
|
} else {
|
|
stringTableKey = s.record.DescStrPos
|
|
}
|
|
|
|
stringTableString := d2tbl.TranslateString(stringTableKey)
|
|
|
|
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
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn6() string {
|
|
var stringTableKey, result string
|
|
|
|
value := s.values[0]
|
|
|
|
formatString := threeComponentStr
|
|
|
|
if value.Int() < 0 {
|
|
stringTableKey = s.record.DescStrNeg
|
|
} else {
|
|
stringTableKey = s.record.DescStrPos
|
|
}
|
|
|
|
str1 := d2tbl.TranslateString(stringTableKey)
|
|
str2 := d2tbl.TranslateString(s.record.DescStr2)
|
|
|
|
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
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn9() string {
|
|
var stringTableKey, result string
|
|
|
|
value := s.values[0]
|
|
|
|
formatString := threeComponentStr
|
|
|
|
if value.Int() < 0 {
|
|
stringTableKey = s.record.DescStrNeg
|
|
} else {
|
|
stringTableKey = s.record.DescStrPos
|
|
}
|
|
|
|
str1 := d2tbl.TranslateString(stringTableKey)
|
|
str2 := d2tbl.TranslateString(s.record.DescStr2)
|
|
|
|
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
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn11() string {
|
|
var stringTableKey string
|
|
|
|
value := s.values[0]
|
|
|
|
if value.Int() < 0 {
|
|
stringTableKey = s.record.DescStrNeg
|
|
} else {
|
|
stringTableKey = s.record.DescStrPos
|
|
}
|
|
|
|
str1 := d2tbl.TranslateString(stringTableKey)
|
|
|
|
formatString := str1
|
|
|
|
return fmt.Sprintf(formatString, value)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn13() string {
|
|
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 ""
|
|
}
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn14() string {
|
|
// strings come out like `+5 to Combat Skills (Paladin Only)`
|
|
numSkills, hero, skillTab := s.values[0], s.values[1], s.values[2]
|
|
heroMap := s.factory.getHeroMap()
|
|
heroIndex := hero.Int()
|
|
classRecord := s.factory.asset.Records.Character.Stats[heroMap[heroIndex]]
|
|
|
|
// 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]
|
|
skillTabStr := d2tbl.TranslateString(skillTabKey)
|
|
skillTabStr = strings.ReplaceAll(skillTabStr, "+%d ", "") // has a token we dont need
|
|
|
|
// `(Paladin Only)`
|
|
classOnlyStr := hero.String()
|
|
|
|
return fmt.Sprintf(threeComponentStr, numSkillsStr, skillTabStr, classOnlyStr)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn15() string {
|
|
chance, lvl, skill := s.values[0], s.values[1], s.values[2]
|
|
|
|
// Special case, `chance to cast` format is actually in the string table!
|
|
chanceToCastStr := d2tbl.TranslateString(s.record.DescStrPos)
|
|
|
|
return fmt.Sprintf(chanceToCastStr, chance.Int(), lvl.Int(), skill)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn16() string {
|
|
skillLevel, skillIndex := s.values[0], s.values[1]
|
|
|
|
// Special case, `Level # XYZ Aura When Equipped`, format is actually in the string table!
|
|
format := d2tbl.TranslateString(s.record.DescStrPos)
|
|
|
|
return fmt.Sprintf(format, skillLevel.Int(), skillIndex)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn22() string {
|
|
arBonus, monsterIndex := s.values[0], s.values[1]
|
|
arVersus := d2tbl.TranslateString(s.record.DescStrPos)
|
|
|
|
return fmt.Sprintf(threeComponentStr, arBonus, arVersus, monsterIndex)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn24() string {
|
|
// Special case formatting
|
|
format := "Level " + threeComponentStr
|
|
|
|
lvl, skill, chargeMax, chargeCurrent := s.values[0],
|
|
s.values[1],
|
|
s.values[2].Int(),
|
|
s.values[3].Int()
|
|
|
|
chargeStr := d2tbl.TranslateString(s.record.DescStrPos)
|
|
chargeStr = fmt.Sprintf(chargeStr, chargeCurrent, chargeMax)
|
|
|
|
return fmt.Sprintf(format, lvl, skill, chargeStr)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn27() string {
|
|
// 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()
|
|
}
|
|
|
|
amount, skill, hero := s.values[0], s.values[1], s.values[2]
|
|
|
|
return fmt.Sprintf(fourComponentStr, amount, "to", skill, hero)
|
|
}
|
|
|
|
func (s *diablo2Stat) descFn28() string {
|
|
amount, skill := s.values[0], s.values[1]
|
|
|
|
return fmt.Sprintf(threeComponentStr, amount, "to", skill)
|
|
}
|
|
|
|
// DescGroupString return a string based on the DescGroupFuncID
|
|
func (s *diablo2Stat) DescGroupString(a ...interface{}) string {
|
|
if s.record.DescGroupFuncID < 0 {
|
|
return ""
|
|
}
|
|
|
|
format := ""
|
|
for range a {
|
|
format += "%s "
|
|
}
|
|
|
|
format = strings.Trim(format, " ")
|
|
|
|
return fmt.Sprintf(format, a...)
|
|
}
|