1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-09 11:47:21 -05:00
OpenDiablo2/d2core/d2item/diablo2item/item_property.go
gravestench fc87b2be7a
Removing d2datadict singletons (#738)
* 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
2020-09-20 17:52:01 -04:00

365 lines
9.4 KiB
Go

package diablo2item
import (
"math/rand"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2stats"
)
const (
noValue = iota
oneValue
twoValue
threeValue
)
const (
skillTabsPerClass = 3
)
// these come from properties.txt, the types of functions that properties can use to evaluate args
const (
fnNone = iota
fnValuesToStat
fnArmorPercent
fnRepeatPreviousWithMinMax // repeat only with min and max
fnUnused
fnDamageMin
fnDamageMax
fnDamagePercent
fnSpeedRelated
fnRepeatPreviousWithParamMinMax // repeat with param, man, and max
fnClassSkillTab
fnProcs
fnRandomSkill
fnMaxDurability
fnNumSockets
fnStatMin
fnStatMax
fnStatParam
fnTimeRelated
fnChargeRelated
fnIndestructable
fnClassSkills
fnSingleSkill
fnEthereal
fnStateApplyToTarget
)
// PropertyType describes what kind of property this is
type PropertyType int
// Property types
// Not all properties contain stats, some are just used to compute a value
// examples are:
// min/max
// % damage
// indestructable and etheral flags
const (
PropertyComputeStats = iota // for properties that do compute stats
PropertyComputeInteger // for properties that compute an integer value
PropertyComputeBoolean // for properties that compute a boolean
)
const (
fnRandClassSkill = 36
)
// Property is an item property.
type Property struct {
factory *ItemFactory
record *d2records.PropertyRecord
stats []d2stats.Stat
PropertyType PropertyType
// the inputValues that were passed initially when calling `NewProperty`
inputParams []int
// some properties are statless and used only for computing a value
computedInt int
computedBool bool
}
func (p *Property) init() *Property {
p.stats = make([]d2stats.Stat, 0)
// some property functions need to be able to repeat last function
// this is for properties with multiple stats that want to repeat the same
// initialization step with the same min/max params
var lastFnCalled int
var stat d2stats.Stat
for idx := range p.record.Stats {
if p.record.Stats[idx] == nil {
continue
}
stat, lastFnCalled = p.eval(idx, lastFnCalled)
// some property stats don't actually have a stat
// but they have functions on the first stat entry
if stat != nil {
p.stats = append(p.stats, stat)
}
}
return p
}
// eval will attempt to create a stat, and will return the function id that was last run.
// this is because some of the properties have a func index which indicates that it should
// repeat the previous fn with the same parameters, but for a different stat.
func (p *Property) eval(propStatIdx, previousFnID int) (stat d2stats.Stat, funcID int) {
pStatRecord := p.record.Stats[propStatIdx]
iscRecord := p.factory.asset.Records.Item.Stats[pStatRecord.StatCode]
funcID = pStatRecord.FunctionID
switch funcID {
case fnRepeatPreviousWithMinMax, fnRepeatPreviousWithParamMinMax:
funcID = previousFnID
fallthrough
case fnValuesToStat, fnSpeedRelated, fnMaxDurability, fnNumSockets,
fnStatMin, fnStatMax, fnSingleSkill, fnArmorPercent:
p.PropertyType = PropertyComputeStats
stat = p.fnValuesToStat(iscRecord)
case fnDamageMin, fnDamageMax, fnDamagePercent:
p.PropertyType = PropertyComputeInteger
p.computedInt = p.fnComputeInteger()
case fnClassSkillTab:
p.PropertyType = PropertyComputeStats
stat = p.fnClassSkillTab(iscRecord)
case fnProcs:
p.PropertyType = PropertyComputeStats
stat = p.fnProcs(iscRecord)
case fnRandomSkill:
p.PropertyType = PropertyComputeStats
stat = p.fnRandomSkill(iscRecord)
case fnStatParam:
p.PropertyType = PropertyComputeStats
stat = p.fnStatParam(iscRecord)
case fnChargeRelated:
p.PropertyType = PropertyComputeStats
stat = p.fnChargeRelated(iscRecord)
case fnIndestructable, fnEthereal:
p.PropertyType = PropertyComputeBoolean
p.computedBool = p.fnBoolean()
case fnClassSkills:
p.PropertyType = PropertyComputeStats
stat = p.fnClassSkills(pStatRecord, iscRecord)
case fnStateApplyToTarget:
p.PropertyType = PropertyComputeStats
stat = p.fnStateApplyToTarget(iscRecord)
case fnRandClassSkill:
p.PropertyType = PropertyComputeStats
stat = p.fnRandClassSkill(iscRecord)
case fnNone, fnUnused, fnTimeRelated:
default:
}
return stat, funcID
}
// fnValuesToStat Applies a value to a stat, can use SetX parameter.
func (p *Property) fnValuesToStat(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
// the only special case to handle for this function is for
// property "color", which corresponds to ISC record "item_lightcolor"
// I'm not yet sure how to handle this special case... it is likely
// and index into one of the colors in colors.txt
var min, max int
var propParam, statValue float64
switch len(p.inputParams) {
case noValue, oneValue:
return nil
case twoValue:
min, max = p.inputParams[0], p.inputParams[1]
case threeValue:
propParam = float64(p.inputParams[0])
min, max = p.inputParams[1], p.inputParams[2]
default:
min, max = p.inputParams[0], p.inputParams[1]
}
if max < min {
min, max = max, min
}
statValue = float64(rand.Intn(max-min+1) + min)
return p.factory.stat.NewStat(iscRecord.Name, statValue, propParam)
}
// fnComputeInteger Dmg-min related ???
func (p *Property) fnComputeInteger() int {
var min, max int
switch len(p.inputParams) {
case noValue, oneValue:
return 0
default:
min, max = p.inputParams[0], p.inputParams[1]
}
statValue := rand.Intn(max-min+1) + min
return statValue
}
// fnClassSkillTab skilltab skill group ???
func (p *Property) fnClassSkillTab(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
// from here: https://d2mods.info/forum/kb/viewarticle?a=45
// Amazon
// 0 - Bow & Crossbow
// 1 - Passive & Magic
// 2 - Spear & Javelin
// Sorceress
// 3 - Fire
// 4 - Lightning
// 5 - Cold
// Necromancer
// 6 - Curses
// 7 - Poison & Bone
// 8 - Summoning
// Paladin
// 9 - Offensive Auras
// 10 - Combat Skills
// 11 - Defensive Auras
// Barbarian
// 12 - Masteries
// 13 - Combat Skills
// 14 - Warcries
// Druid
// 15 - Summoning
// 16 - Shapeshifting
// 17 - Elemental
// Assassin
// 18 - Traps
// 19 - Shadow Disciplines
// 20 - Martial Arts
param, min, max := p.inputParams[0], p.inputParams[1], p.inputParams[2]
skillTabIdx := float64(param % skillTabsPerClass)
classIdx := float64(param / skillTabsPerClass)
level := float64(rand.Intn(max-min+1) + min)
return p.factory.stat.NewStat(iscRecord.Name, level, classIdx, skillTabIdx)
}
// fnProcs event-based skills ???
func (p *Property) fnProcs(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
var skillID, chance, skillLevel float64
switch len(p.inputParams) {
case noValue, oneValue, twoValue:
return nil
default:
skillID = float64(p.inputParams[0])
chance = float64(p.inputParams[1])
skillLevel = float64(p.inputParams[2])
}
return p.factory.stat.NewStat(iscRecord.Name, chance, skillLevel, skillID)
}
// fnRandomSkill random selection of parameters for parameter-based stat ???
func (p *Property) fnRandomSkill(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
var skillLevel, skillID float64
invalidHeroIndex := -1.0
switch len(p.inputParams) {
case noValue, oneValue, twoValue:
return nil
default:
skillLevel = float64(p.inputParams[0])
min, max := p.inputParams[1], p.inputParams[2]
skillID = float64(rand.Intn(max-min+1) + min)
}
return p.factory.stat.NewStat(iscRecord.Name, skillLevel, skillID, invalidHeroIndex)
}
// fnStatParam use param field only
func (p *Property) fnStatParam(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
switch len(p.inputParams) {
case noValue:
return nil
default:
val := float64(p.inputParams[0])
return p.factory.stat.NewStat(iscRecord.Name, val)
}
}
// fnChargeRelated Related to charged item.
func (p *Property) fnChargeRelated(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
var lvl, skill, charges float64
switch len(p.inputParams) {
case noValue, oneValue, twoValue:
return nil
default:
lvl = float64(p.inputParams[2])
skill = float64(p.inputParams[0])
charges = float64(p.inputParams[1])
return p.factory.stat.NewStat(iscRecord.Name, lvl, skill, charges, charges)
}
}
// fnIndestructable Simple boolean stuff. Use by indestruct.
func (p *Property) fnBoolean() bool {
var min, max int
switch len(p.inputParams) {
case noValue, oneValue:
return false
default:
min, max = p.inputParams[0], p.inputParams[1]
}
statValue := rand.Intn(max-min+1) + min
return statValue > 0
}
// fnClassSkills Add to group of skills, group determined by stat ID, uses ValX parameter.
func (p *Property) fnClassSkills(
propStatRecord *d2records.PropertyStatRecord,
iscRecord *d2records.ItemStatCostRecord,
) d2stats.Stat {
// in order 0..6
// Amazon
// Sorceress
// Necromancer
// Paladin
// Druid
// Assassin
var min, max, classIdx int
switch len(p.inputParams) {
case noValue, oneValue:
return nil
default:
min, max = p.inputParams[0], p.inputParams[1]
}
statValue := rand.Intn(max-min+1) + min
classIdx = propStatRecord.Value
return p.factory.stat.NewStat(iscRecord.Name, float64(statValue), float64(classIdx))
}
// fnStateApplyToTarget property applied to character or target monster ???
func (p *Property) fnStateApplyToTarget(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
// todo need to implement states
return nil
}
// fnRandClassSkill property applied to character or target monster ???
func (p *Property) fnRandClassSkill(iscRecord *d2records.ItemStatCostRecord) d2stats.Stat {
return nil
}