diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml
index d42ea917..6e0473cb 100755
--- a/_datafiles/config.yaml
+++ b/_datafiles/config.yaml
@@ -536,6 +536,27 @@ Validation:
- "join"
- "register"
+################################################################################
+#
+# Statistics Configuration
+# Settings for character statistics and stat calculations
+#
+################################################################################
+Statistics:
+ # Base stat values for new characters
+ BaseStats:
+ Strength: 1 # Muscular strength
+ Speed: 1 # Speed and agility
+ Smarts: 1 # Intelligence and wisdom
+ Vitality: 1 # Health and stamina
+ Mysticism: 1 # Magic and mana
+ Perception: 1 # How well you notice things
+
+ # Stat calculation factors
+ Factors:
+ BaseModFactor: 0.3333333334 # How much of a scaling to apply to levels before multiplying by racial stat
+ NaturalGainsModFactor: 0.5 # Free stats gained per level modded by this
+
################################################################################
#
# Roles
diff --git a/_datafiles/html/admin/races/race.data.html b/_datafiles/html/admin/races/race.data.html
index 104aab4e..070240c4 100644
--- a/_datafiles/html/admin/races/race.data.html
+++ b/_datafiles/html/admin/races/race.data.html
@@ -70,41 +70,41 @@
Base Stats
Strength
-
+
diff --git a/_datafiles/world/default/templates/character/status.template b/_datafiles/world/default/templates/character/status.template
index dad209e8..48144f72 100644
--- a/_datafiles/world/default/templates/character/status.template
+++ b/_datafiles/world/default/templates/character/status.template
@@ -6,13 +6,13 @@
{{- $hpDisplay := printf "%s" ( healthStr .Character.Health .Character.HealthMax.Value 22 ) }}
{{- $mpDisplay := printf "%s" ( manaStr .Character.Mana .Character.ManaMax.Value 22 ) }}
┌─ .: Info ──────────────────────┐ ┌─ .: Attributes ───────────────────────────┐
- │ Area: {{ printf "%-22s" .Character.Zone }}│ │ Strength: {{ printf "%-4d (%-3d) " .Character.Stats.Strength.Value (.Character.StatMod "strength") }} Vitality: {{ printf "%-4d (%-3d) " .Character.Stats.Vitality.Value (.Character.StatMod "vitality") }} │
- Race: {{ printf "%-22s" .Character.Race }} Speed: {{ printf "%-4d (%-3d) " .Character.Stats.Speed.Value (.Character.StatMod "speed") }} Mysticism: {{ printf "%-4d (%-3d) " .Character.Stats.Mysticism.Value (.Character.StatMod "mysticism") }}
- Level: {{ printf "%-22d" .Character.Level }} │ Smarts: {{ printf "%-4d (%-3d) " .Character.Stats.Smarts.Value (.Character.StatMod "smarts") }} Percept: {{ printf "%-4d (%-3d) " .Character.Stats.Perception.Value (.Character.StatMod "perception") }} │
+ │ Area: {{ printf "%-22s" .Character.Zone }}│ │ Strength: {{ printf "%-4d (%-3d) " (charStatValue .Character "strength") (.Character.StatMod "strength") }} Vitality: {{ printf "%-4d (%-3d) " (charStatValue .Character "Vitality") (.Character.StatMod "vitality") }} │
+ Race: {{ printf "%-22s" .Character.Race }} Speed: {{ printf "%-4d (%-3d) " (charStatValue .Character "Speed") (.Character.StatMod "speed") }} Mysticism: {{ printf "%-4d (%-3d) " (charStatValue .Character "Mysticism") (.Character.StatMod "mysticism") }}
+ Level: {{ printf "%-22d" .Character.Level }} │ Smarts: {{ printf "%-4d (%-3d) " (charStatValue .Character "Smarts") (.Character.StatMod "smarts") }} Percept: {{ printf "%-4d (%-3d) " (charStatValue .Character "Perception") (.Character.StatMod "perception") }} │
Exp: {{ printf "%-22s" ( tnl .UserId ) }} └──────────────────────────────────────────┘
Health: {{ printf "%s" $hpDisplay }} ┌─ .: Wealth ────────┐ ┌─ .: Training ───────┐
Mana: {{ printf "%s" $mpDisplay }} │ Gold: {{ printf "%-11s" (numberFormat .Character.Gold) }} │ │ Train Pts: {{ printf "%-7d" .Character.TrainingPoints }} │
│ Armor: {{ printf "%-6s" ( printf "%d" (.Character.GetDefense)) }} {{ if permadeath }}Lives: {{ printf "%-7d" .Character.ExtraLives }}{{ else }} {{ end }} │ │ Bank: {{ printf "%-11s" (numberFormat .Character.Bank) }} │ │ Stat Pts: {{ printf "%-7d" .Character.StatPoints }} │
└───────────────────────────────┘ └───────────────────┘ └────────────────────┘
{{- if gt .Character.StatPoints 0 }}{{ if lt .Character.Level 5 }}
- TIP: Type status train to spend stat points on improvements. {{ end }}{{ end -}}
\ No newline at end of file
+ TIP: Type status train to spend stat points on improvements. {{ end }}{{ end -}}
diff --git a/internal/characters/character.go b/internal/characters/character.go
index 081439df..4c5978b5 100644
--- a/internal/characters/character.go
+++ b/internal/characters/character.go
@@ -95,6 +95,8 @@ type Character struct {
}
func New() *Character {
+ statsConfig := configs.GetStatisticsConfig()
+
return &Character{
//Name: defaultName,
Adjectives: []string{},
@@ -102,12 +104,12 @@ func New() *Character {
Zone: startingZone,
RaceId: startingRace,
Stats: stats.Statistics{
- Strength: stats.StatInfo{Base: 1},
- Speed: stats.StatInfo{Base: 1},
- Smarts: stats.StatInfo{Base: 1},
- Vitality: stats.StatInfo{Base: 1},
- Mysticism: stats.StatInfo{Base: 1},
- Perception: stats.StatInfo{Base: 1},
+ Strength: stats.StatInfo{Base: int(statsConfig.BaseStats.Strength)},
+ Speed: stats.StatInfo{Base: int(statsConfig.BaseStats.Speed)},
+ Smarts: stats.StatInfo{Base: int(statsConfig.BaseStats.Smarts)},
+ Vitality: stats.StatInfo{Base: int(statsConfig.BaseStats.Vitality)},
+ Mysticism: stats.StatInfo{Base: int(statsConfig.BaseStats.Mysticism)},
+ Perception: stats.StatInfo{Base: int(statsConfig.BaseStats.Perception)},
},
Level: 1,
Experience: 1,
@@ -206,7 +208,7 @@ func (c *Character) GetBaseCastSuccessChance(spellId string) int {
}
targetNumber += proficiency
- targetNumber += int(math.Floor(float64(c.Stats.Mysticism.ValueAdj) / 5))
+ targetNumber += int(math.Floor(float64(c.Stats.Get("Mysticism").ValueAdj) / 5))
// add by any stat mods for casting, or casting school
// 0-xx
@@ -222,7 +224,7 @@ func (c *Character) GetBaseCastSuccessChance(spellId string) int {
}
func (c *Character) CarryCapacity() int {
- return 5 + c.Stats.Strength.ValueAdj/3
+ return 5 + c.Stats.Get("Strength").ValueAdj/3
}
func (c *Character) DeductActionPoints(amount int) bool {
@@ -367,9 +369,9 @@ func (c *Character) GetDefaultDiceRoll() (attacks int, dCount int, dSides int, b
bonus = raceInfo.Damage.BonusDamage
buffOnCrit = raceInfo.Damage.CritBuffIds
- dCount += int(math.Floor((float64(c.Stats.Speed.ValueAdj) / 50)))
- dSides += int(math.Floor((float64(c.Stats.Strength.ValueAdj) / 12)))
- bonus += int(math.Floor((float64(c.Stats.Perception.ValueAdj) / 25)))
+ dCount += int(math.Floor((float64(c.Stats.Get("Speed").ValueAdj) / 50)))
+ dSides += int(math.Floor((float64(c.Stats.Get("Strength").ValueAdj) / 12)))
+ bonus += int(math.Floor((float64(c.Stats.Get("Perception").ValueAdj) / 25)))
if dCount < raceInfo.Damage.DiceCount {
dCount = raceInfo.Damage.DiceCount
@@ -958,7 +960,7 @@ func (c *Character) GetMaxCharmedCreatures() int {
}
func (c *Character) GetMemoryCapacity() int {
- memCap := c.GetSkillLevel(skills.Map) * c.Stats.Smarts.ValueAdj
+ memCap := c.GetSkillLevel(skills.Map) * c.Stats.Get("Smarts").ValueAdj
if memCap < 0 {
memCap = 0
}
@@ -966,7 +968,7 @@ func (c *Character) GetMemoryCapacity() int {
}
func (c *Character) GetMapSprawlCapacity() int {
- sprawlCap := c.GetSkillLevel(skills.Map) + (c.Stats.Smarts.ValueAdj >> 2)
+ sprawlCap := c.GetSkillLevel(skills.Map) + (c.Stats.Get("Smarts").ValueAdj >> 2)
if sprawlCap < 0 {
sprawlCap = 0
}
@@ -1256,7 +1258,7 @@ func (c *Character) ApplyManaChange(manaChange int) int {
}
func (c *Character) BarterPrice(startPrice int) int {
- factor := (float64(c.Stats.Perception.ValueAdj) / 3) / 100 // 100 = 33% discount, 0 = 0% discount, 300 = 100% discount
+ factor := (float64(c.Stats.Get("Perception").ValueAdj) / 3) / 100 // 100 = 33% discount, 0 = 0% discount, 300 = 100% discount
if factor > .75 {
factor = .75
}
@@ -1308,12 +1310,12 @@ func (c *Character) LevelUp() (bool, stats.Statistics) {
var statsDelta stats.Statistics = c.Stats
- statsDelta.Strength.Value -= statsBefore.Strength.Value
- statsDelta.Speed.Value -= statsBefore.Speed.Value
- statsDelta.Smarts.Value -= statsBefore.Smarts.Value
- statsDelta.Vitality.Value -= statsBefore.Vitality.Value
- statsDelta.Mysticism.Value -= statsBefore.Mysticism.Value
- statsDelta.Perception.Value -= statsBefore.Perception.Value
+ statsDelta.Get("Strength").Value -= statsBefore.Get("Strength").Value
+ statsDelta.Get("Speed").Value -= statsBefore.Get("Speed").Value
+ statsDelta.Get("Smarts").Value -= statsBefore.Get("Smarts").Value
+ statsDelta.Get("Vitality").Value -= statsBefore.Get("Vitality").Value
+ statsDelta.Get("Mysticism").Value -= statsBefore.Get("Mysticism").Value
+ statsDelta.Get("Perception").Value -= statsBefore.Get("Perception").Value
c.Health = c.HealthMax.Value
c.Mana = c.ManaMax.Value
@@ -1340,7 +1342,7 @@ func (c *Character) Heal(hp int, mana int) (int, int) {
func (c *Character) HealthPerRound() int {
return 1 + c.StatMod(string(statmods.HealthRecovery))
/*
- healAmt := math.Round(float64(c.Stats.Vitality.ValueAdj)/8) +
+ healAmt := math.Round(float64(c.Stats.Get("Vitality").ValueAdj)/8) +
math.Round(float64(c.Level)/12) +
1.0
@@ -1351,7 +1353,7 @@ func (c *Character) HealthPerRound() int {
func (c *Character) ManaPerRound() int {
return 1 + c.StatMod(string(statmods.ManaRecovery))
/*
- healAmt := math.Round(float64(c.Stats.Mysticism.ValueAdj)/8) +
+ healAmt := math.Round(float64(c.Stats.Get("Mysticism").ValueAdj)/8) +
math.Round(float64(c.Level)/12) +
1.0
@@ -1361,9 +1363,9 @@ func (c *Character) ManaPerRound() int {
// Where 1000 = a full round
func (c *Character) MovementCost() int {
- modifier := 3 // by default they should be able to move 3 times per round.
- modifier += int(c.Level / 15) // Every 15 levels, get an extra movement.
- modifier += int(c.Stats.Speed.ValueAdj / 15) // Every 15 speed, get an extra movement
+ modifier := 3 // by default they should be able to move 3 times per round.
+ modifier += int(c.Level / 15) // Every 15 levels, get an extra movement.
+ modifier += int(c.Stats.Get("Speed").ValueAdj / 15) // Every 15 speed, get an extra movement
return int(1000 / modifier)
}
@@ -1372,6 +1374,8 @@ func (c *Character) StatMod(statName string) int {
}
// returns true if something has changed.
+// TODO: [nitpick] There are many repetitive Get("X") calls; consider iterating over a slice of stat names or using a helper to apply updates in a loop for readability.
+
func (c *Character) RecalculateStats() {
// Make sure racial base stats are set
@@ -1385,43 +1389,34 @@ func (c *Character) RecalculateStats() {
if c.TNLScale == 0 {
c.TNLScale = 1.0
}
- c.Stats.Strength.Base = raceInfo.Stats.Strength.Base
- c.Stats.Speed.Base = raceInfo.Stats.Speed.Base
- c.Stats.Smarts.Base = raceInfo.Stats.Smarts.Base
- c.Stats.Vitality.Base = raceInfo.Stats.Vitality.Base
- c.Stats.Mysticism.Base = raceInfo.Stats.Mysticism.Base
- c.Stats.Perception.Base = raceInfo.Stats.Perception.Base
+ for _, statName := range c.Stats.GetStatInfoNames() {
+ c.Stats.Get(statName).Base = raceInfo.Stats.Get(statName).Base
+ }
}
// Add any mods for equipment
- c.Stats.Strength.Mods = c.StatMod(string(statmods.Strength))
- c.Stats.Speed.Mods = c.StatMod(string(statmods.Speed))
- c.Stats.Smarts.Mods = c.StatMod(string(statmods.Smarts))
- c.Stats.Vitality.Mods = c.StatMod(string(statmods.Vitality))
- c.Stats.Mysticism.Mods = c.StatMod(string(statmods.Mysticism))
- c.Stats.Perception.Mods = c.StatMod(string(statmods.Perception))
+ for _, statName := range c.Stats.GetStatInfoNames() {
+ c.Stats.Get(statName).Mods = c.StatMod(statName)
+ }
// Recalculate stats
// Stats are basically:
// level*base + training + mods
- c.Stats.Strength.Recalculate(c.Level)
- c.Stats.Speed.Recalculate(c.Level)
- c.Stats.Smarts.Recalculate(c.Level)
- c.Stats.Vitality.Recalculate(c.Level)
- c.Stats.Mysticism.Recalculate(c.Level)
- c.Stats.Perception.Recalculate(c.Level)
+ for _, statName := range c.Stats.GetStatInfoNames() {
+ c.Stats.Get(statName).Recalculate(c.Level)
+ }
// Set HP/MP maxes
// This relies on the above stats so has to be calculated afterwards
c.HealthMax.Mods = 5 +
c.StatMod(string(statmods.HealthMax)) + // Any sort of spell buffs etc. are just direct modifiers
c.Level + // For every level you get 1 hp
- c.Stats.Vitality.ValueAdj*4 // for every vitality you get 3hp
+ c.Stats.Get("Vitality").ValueAdj*4 // for every vitality you get 3hp
c.ManaMax.Mods = 4 +
c.StatMod(string(statmods.ManaMax)) + // Any sort of spell buffs etc. are just direct modifiers
c.Level + // For every level you get 1 mp
- c.Stats.Mysticism.ValueAdj*3 // for every Mysticism you get 2mp
+ c.Stats.Get("Mysticism").ValueAdj*3 // for every Mysticism you get 2mp
// Set max action points
c.ActionPointsMax.Mods = 200 // hard coded for now
@@ -1445,22 +1440,16 @@ func (c *Character) RecalculateStats() {
if c.userId != 0 {
changed := false
// return true if something has changed.
- if beforeStats.Strength.ValueAdj != c.Stats.Strength.ValueAdj {
- changed = true
- } else if beforeStats.Speed.ValueAdj != c.Stats.Speed.ValueAdj {
- changed = true
- } else if beforeStats.Smarts.ValueAdj != c.Stats.Smarts.ValueAdj {
- changed = true
- } else if beforeStats.Vitality.ValueAdj != c.Stats.Vitality.ValueAdj {
- changed = true
- } else if beforeStats.Mysticism.ValueAdj != c.Stats.Mysticism.ValueAdj {
- changed = true
- } else if beforeStats.Perception.ValueAdj != c.Stats.Perception.ValueAdj {
- changed = true
- } else if beforeHealthMax != c.HealthMax {
- changed = true
- } else if beforeManaMax != c.ManaMax {
- changed = true
+ for _, statName := range c.Stats.GetStatInfoNames() {
+ if beforeStats.Get(statName).ValueAdj != c.Stats.Get(statName).ValueAdj {
+ changed = true
+ break
+ }
+ }
+ if !changed {
+ if beforeHealthMax != c.HealthMax || beforeManaMax != c.ManaMax {
+ changed = true
+ }
}
if changed {
@@ -1481,17 +1470,17 @@ func (c *Character) AutoTrain() {
switch util.Rand(6) {
case 0:
- c.Stats.Strength.Training++
+ c.Stats.Get("Strength").Training++
case 1:
- c.Stats.Speed.Training++
+ c.Stats.Get("Speed").Training++
case 2:
- c.Stats.Smarts.Training++
+ c.Stats.Get("Smarts").Training++
case 3:
- c.Stats.Vitality.Training++
+ c.Stats.Get("Vitality").Training++
case 4:
- c.Stats.Mysticism.Training++
+ c.Stats.Get("Mysticism").Training++
case 5:
- c.Stats.Perception.Training++
+ c.Stats.Get("Perception").Training++
}
c.StatPoints--
diff --git a/internal/characters/character_test.go b/internal/characters/character_test.go
index 947d44e4..5f6bbf91 100644
--- a/internal/characters/character_test.go
+++ b/internal/characters/character_test.go
@@ -194,7 +194,7 @@ func TestCharacter_CarryCapacity(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := New()
- c.Stats.Strength.ValueAdj = tt.strengthAdj
+ c.Stats.Get("Strength").ValueAdj = tt.strengthAdj
got := c.CarryCapacity()
assert.Equal(t, tt.expectedCap, got)
})
diff --git a/internal/combat/calculations.go b/internal/combat/calculations.go
index edac2ea6..a5617a02 100644
--- a/internal/combat/calculations.go
+++ b/internal/combat/calculations.go
@@ -24,10 +24,10 @@ func PowerRanking(atkChar characters.Character, defChar characters.Character) fl
pct += 0.4 * float64(atkDmg) / float64(defDmg)
}
- if defChar.Stats.Speed.ValueAdj == 0 {
+ if defChar.Stats.Get("Speed").ValueAdj == 0 {
pct += 0.3
} else {
- pct += 0.3 * float64(atkChar.Stats.Speed.ValueAdj) / float64(defChar.Stats.Speed.ValueAdj)
+ pct += 0.3 * float64(atkChar.Stats.Get("Speed").ValueAdj) / float64(defChar.Stats.Get("Speed").ValueAdj)
}
if defChar.HealthMax.Value == 0 {
diff --git a/internal/combat/combat.go b/internal/combat/combat.go
index cb7ddfb6..d2ee773f 100644
--- a/internal/combat/combat.go
+++ b/internal/combat/combat.go
@@ -223,7 +223,7 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char
attackResult := AttackResult{}
- attackCount := int(math.Ceil(float64(sourceChar.Stats.Speed.ValueAdj-targetChar.Stats.Speed.ValueAdj) / 25))
+ attackCount := int(math.Ceil(float64(sourceChar.Stats.Get("Speed").ValueAdj-targetChar.Stats.Get("Speed").ValueAdj) / 25))
if attackCount < 1 {
attackCount = 1
}
@@ -352,7 +352,7 @@ func calculateCombat(sourceChar characters.Character, targetChar characters.Char
attackSourceDamage := 0
attackSourceReduction := 0
- if Hits(sourceChar.Stats.Speed.ValueAdj, targetChar.Stats.Speed.ValueAdj, penalty) {
+ if Hits(sourceChar.Stats.Get("Speed").ValueAdj, targetChar.Stats.Get("Speed").ValueAdj, penalty) {
attackResult.Hit = true
attackTargetDamage = util.RollDice(dCount, dSides) + dBonus
@@ -580,7 +580,7 @@ func Crits(sourceChar characters.Character, targetChar characters.Character) boo
if levelDiff < 1 {
levelDiff = 1
}
- critChance := 5 + int(math.Round(float64(sourceChar.Stats.Strength.ValueAdj+sourceChar.Stats.Speed.ValueAdj)/float64(levelDiff)))
+ critChance := 5 + int(math.Round(float64(sourceChar.Stats.Get("Strength").ValueAdj+sourceChar.Stats.Get("Speed").ValueAdj)/float64(levelDiff)))
if sourceChar.HasBuffFlag(buffs.Accuracy) {
critChance *= 2
diff --git a/internal/configs/config.statistics.go b/internal/configs/config.statistics.go
new file mode 100644
index 00000000..023e9702
--- /dev/null
+++ b/internal/configs/config.statistics.go
@@ -0,0 +1,59 @@
+package configs
+
+type Statistics struct {
+ BaseStats StatDefaults `yaml:"BaseStats"`
+ Factors StatFactors `yaml:"Factors"`
+}
+
+type StatDefaults struct {
+ Strength ConfigInt `yaml:"Strength"`
+ Speed ConfigInt `yaml:"Speed"`
+ Smarts ConfigInt `yaml:"Smarts"`
+ Vitality ConfigInt `yaml:"Vitality"`
+ Mysticism ConfigInt `yaml:"Mysticism"`
+ Perception ConfigInt `yaml:"Perception"`
+}
+
+type StatFactors struct {
+ BaseModFactor ConfigFloat `yaml:"BaseModFactor"` // How much of a scaling to aply to levels before multiplying by racial stat
+ NaturalGainsModFactor ConfigFloat `yaml:"NaturalGainsModFactor"` // Free stats gained per level modded by this.
+}
+
+func (s *Statistics) Validate() {
+ if s.BaseStats.Strength < 1 {
+ s.BaseStats.Strength = 1
+ }
+ if s.BaseStats.Speed < 1 {
+ s.BaseStats.Speed = 1
+ }
+ if s.BaseStats.Smarts < 1 {
+ s.BaseStats.Smarts = 1
+ }
+ if s.BaseStats.Vitality < 1 {
+ s.BaseStats.Vitality = 1
+ }
+ if s.BaseStats.Mysticism < 1 {
+ s.BaseStats.Mysticism = 1
+ }
+ if s.BaseStats.Perception < 1 {
+ s.BaseStats.Perception = 1
+ }
+
+ // Validate factors are reasonable
+ if s.Factors.BaseModFactor <= 0 {
+ s.Factors.BaseModFactor = 0.3333333334 // default
+ }
+ if s.Factors.NaturalGainsModFactor <= 0 {
+ s.Factors.NaturalGainsModFactor = 0.5 // default
+ }
+}
+
+func GetStatisticsConfig() Statistics {
+ configDataLock.RLock()
+ defer configDataLock.RUnlock()
+
+ if !configData.validated {
+ configData.Validate()
+ }
+ return configData.Statistics
+}
diff --git a/internal/configs/configs.go b/internal/configs/configs.go
index 5018a06a..419529a1 100644
--- a/internal/configs/configs.go
+++ b/internal/configs/configs.go
@@ -48,6 +48,7 @@ type Config struct {
SpecialRooms SpecialRooms `yaml:"SpecialRooms"`
Validation Validation `yaml:"Validation"`
Roles Roles `yaml:"Roles"`
+ Statistics Statistics `yaml:"Statistics"`
// Plugins is a special case
Modules Modules `yaml:"Modules"`
@@ -197,6 +198,7 @@ func (c *Config) Validate() {
c.Scripting.Validate()
c.SpecialRooms.Validate()
c.Validation.Validate()
+ c.Statistics.Validate()
c.Modules.Validate()
c.Roles.Validate()
diff --git a/internal/hooks/NewRound_DoCombat.go b/internal/hooks/NewRound_DoCombat.go
index 33a40a62..669d6c2f 100644
--- a/internal/hooks/NewRound_DoCombat.go
+++ b/internal/hooks/NewRound_DoCombat.go
@@ -80,7 +80,7 @@ func handlePlayerCombat(evt events.NewRound) (affectedPlayerIds []int, affectedM
}
// Stat comparison accounts for up to 70% of chance to flee.
- chanceIn100 := int(float64(user.Character.Stats.Speed.ValueAdj) / (float64(user.Character.Stats.Speed.ValueAdj) + float64(mob.Character.Stats.Speed.ValueAdj)) * 70)
+ chanceIn100 := int(float64(user.Character.Stats.Get("Speed").ValueAdj) / (float64(user.Character.Stats.Get("Speed").ValueAdj) + float64(mob.Character.Stats.Get("Speed").ValueAdj)) * 70)
chanceIn100 += 30
roll := util.Rand(100)
@@ -103,7 +103,7 @@ func handlePlayerCombat(evt events.NewRound) (affectedPlayerIds []int, affectedM
}
// if equal, 25% chance of fleeing... at best, 50% chance. Then add 50% on top.
- chanceIn100 := int(float64(user.Character.Stats.Speed.ValueAdj) / (float64(user.Character.Stats.Speed.ValueAdj) + float64(u.Character.Stats.Speed.ValueAdj)) * 70)
+ chanceIn100 := int(float64(user.Character.Stats.Get("Speed").ValueAdj) / (float64(user.Character.Stats.Get("Speed").ValueAdj) + float64(u.Character.Stats.Get("Speed").ValueAdj)) * 70)
chanceIn100 += 30
roll := util.Rand(100)
@@ -567,7 +567,7 @@ func handlePlayerCombat(evt events.NewRound) (affectedPlayerIds []int, affectedM
//
// Hostility default to 5 minutes
for _, groupName := range defMob.Groups {
- mobs.MakeHostile(groupName, user.UserId, c.Timing.MinutesToRounds(2)-user.Character.Stats.Perception.ValueAdj)
+ mobs.MakeHostile(groupName, user.UserId, c.Timing.MinutesToRounds(2)-user.Character.Stats.Get("Perception").ValueAdj)
}
// Mobs get aggro when attacked
@@ -793,9 +793,9 @@ func handleMobCombat(evt events.NewRound) (affectedPlayerIds []int, affectedMobI
roll := util.Rand(100)
- util.LogRoll(`Look for weapon`, roll, mob.Character.Stats.Perception.ValueAdj)
+ util.LogRoll(`Look for weapon`, roll, mob.Character.Stats.Get("Perception").ValueAdj)
- if roll < mob.Character.Stats.Perception.ValueAdj {
+ if roll < mob.Character.Stats.Get("Perception").ValueAdj {
possibleWeapons := []string{}
for _, itm := range mob.Character.Items {
iSpec := itm.GetSpec()
diff --git a/internal/mobcommands/suicide.go b/internal/mobcommands/suicide.go
index 820ab2dd..97232521 100644
--- a/internal/mobcommands/suicide.go
+++ b/internal/mobcommands/suicide.go
@@ -171,7 +171,7 @@ func Suicide(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
if levelDelta < 0 {
levelDelta = 0
}
- skillsDelta := int((float64(user.Character.Stats.Perception.ValueAdj-mob.Character.Stats.Perception.ValueAdj) + float64(user.Character.Stats.Smarts.ValueAdj-mob.Character.Stats.Smarts.ValueAdj)) / 2)
+ skillsDelta := int((float64(user.Character.Stats.Get("Perception").ValueAdj-mob.Character.Stats.Get("Perception").ValueAdj) + float64(user.Character.Stats.Get("Smarts").ValueAdj-mob.Character.Stats.Get("Smarts").ValueAdj)) / 2)
if skillsDelta < 0 {
skillsDelta = 0
}
@@ -251,7 +251,7 @@ func Suicide(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) {
if levelDelta < 0 {
levelDelta = 0
}
- skillsDelta := int((float64(user.Character.Stats.Perception.ValueAdj-mob.Character.Stats.Perception.ValueAdj) + float64(user.Character.Stats.Smarts.ValueAdj-mob.Character.Stats.Smarts.ValueAdj)) / 2)
+ skillsDelta := int((float64(user.Character.Stats.Get("Perception").ValueAdj-mob.Character.Stats.Get("Perception").ValueAdj) + float64(user.Character.Stats.Get("Smarts").ValueAdj-mob.Character.Stats.Get("Smarts").ValueAdj)) / 2)
if skillsDelta < 0 {
skillsDelta = 0
}
diff --git a/internal/races/races.go b/internal/races/races.go
index 0dc7b20a..8e0e030f 100644
--- a/internal/races/races.go
+++ b/internal/races/races.go
@@ -96,12 +96,9 @@ func (r *Race) Validate() error {
r.Size = Size(strings.ToLower(string(r.Size))) // Sometimes a mismatching CaSe value is provided.
// Recalculate stats, based on level one because this is actually the baseline for the race
- r.Stats.Strength.Recalculate(1)
- r.Stats.Speed.Recalculate(1)
- r.Stats.Smarts.Recalculate(1)
- r.Stats.Vitality.Recalculate(1)
- r.Stats.Mysticism.Recalculate(1)
- r.Stats.Perception.Recalculate(1)
+ for _, statName := range r.Stats.GetStatInfoNames() {
+ r.Stats.Get(statName).Recalculate(1)
+ }
if r.Damage.Attacks < 1 && r.Damage.DiceCount > 0 && r.Damage.SideCount > 0 {
r.Damage.Attacks = 1
diff --git a/internal/scripting/actor_func.go b/internal/scripting/actor_func.go
index f1260099..924f923a 100644
--- a/internal/scripting/actor_func.go
+++ b/internal/scripting/actor_func.go
@@ -73,32 +73,33 @@ func (a ScriptActor) GetLevel() int {
return a.characterRecord.Level
}
+// TODO: Need to allow for this stat list to be dynamic
func (a ScriptActor) GetStat(statName string) int {
statName = strings.ToLower(statName)
if strings.HasPrefix(statName, "st") {
- return a.characterRecord.Stats.Strength.ValueAdj
+ return a.characterRecord.Stats.Get("Strength").ValueAdj
}
if strings.HasPrefix(statName, "sp") {
- return a.characterRecord.Stats.Speed.ValueAdj
+ return a.characterRecord.Stats.Get("Speed").ValueAdj
}
if strings.HasPrefix(statName, "sm") {
- return a.characterRecord.Stats.Smarts.ValueAdj
+ return a.characterRecord.Stats.Get("Smarts").ValueAdj
}
if strings.HasPrefix(statName, "vi") {
- return a.characterRecord.Stats.Vitality.ValueAdj
+ return a.characterRecord.Stats.Get("Vitality").ValueAdj
}
if strings.HasPrefix(statName, "my") {
- return a.characterRecord.Stats.Mysticism.ValueAdj
+ return a.characterRecord.Stats.Get("Mysticism").ValueAdj
}
if strings.HasPrefix(statName, "pe") {
- return a.characterRecord.Stats.Perception.ValueAdj
+ return a.characterRecord.Stats.Get("Perception").ValueAdj
}
return 0
diff --git a/internal/stats/stats.go b/internal/stats/stats.go
index 3dc24503..428bfcf3 100644
--- a/internal/stats/stats.go
+++ b/internal/stats/stats.go
@@ -1,12 +1,22 @@
package stats
-import "math"
+import (
+ "math"
+ "strings"
-const (
- BaseModFactor = 0.3333333334 // How much of a scaling to aply to levels before multiplying by racial stat
- NaturalGainsModFactor = 0.5 // Free stats gained per level modded by this.
+ "github.com/GoMudEngine/GoMud/internal/configs"
)
+func getBaseModFactor() float64 {
+ cfg := configs.GetStatisticsConfig()
+ return float64(cfg.Factors.BaseModFactor)
+}
+
+func getNaturalGainsModFactor() float64 {
+ cfg := configs.GetStatisticsConfig()
+ return float64(cfg.Factors.NaturalGainsModFactor)
+}
+
type Statistics struct {
Strength StatInfo `yaml:"strength,omitempty"` // Muscular strength (damage?)
Speed StatInfo `yaml:"speed,omitempty"` // Speed and agility (dodging)
@@ -16,6 +26,48 @@ type Statistics struct {
Perception StatInfo `yaml:"perception,omitempty"` // How well you notice things
}
+// GetStatInfoNames returns a list of all stat names in the order they are defined.
+// TODO: This should be a representation of the stats in the game, not hardcoded.
+func (s *Statistics) GetStatInfoNames() []string {
+ names := []string{
+ "Strength",
+ "Speed",
+ "Smarts",
+ "Vitality",
+ "Mysticism",
+ "Perception",
+ }
+ return names
+}
+
+// Get returns a pointer to the StatInfo for the given name.
+func (s *Statistics) Get(name string) *StatInfo {
+ key := strings.ToLower(name)
+
+ // TODO: When we load the stats from a file, we need to check the map
+ // if stat, ok := s.Stats[key]; ok {
+ // copy := stat
+ // return ©
+ // }
+
+ switch key {
+ case "strength":
+ return &s.Strength
+ case "speed":
+ return &s.Speed
+ case "smarts":
+ return &s.Smarts
+ case "vitality":
+ return &s.Vitality
+ case "mysticism":
+ return &s.Mysticism
+ case "perception":
+ return &s.Perception
+ }
+
+ return &StatInfo{}
+}
+
// When saving to a file, we don't need to write all the properties that we calculate.
// Just keep track of "Training" because that's not calculated.
type StatInfo struct {
@@ -42,11 +94,11 @@ func (si *StatInfo) GainsForLevel(level int) int {
if level < 1 {
level = 1
}
- levelScale := float64(level-1) * BaseModFactor
+ levelScale := float64(level-1) * getBaseModFactor()
basePoints := int(levelScale * float64(si.Base))
// every x levels we get natural gains
- freeStatPoints := int(float64(level) * NaturalGainsModFactor)
+ freeStatPoints := int(float64(level) * getNaturalGainsModFactor())
return basePoints + freeStatPoints
}
@@ -60,3 +112,36 @@ func (si *StatInfo) Recalculate(level int) {
si.ValueAdj = 100 + int(math.Round(math.Sqrt(float64(overage))*2))
}
}
+
+// Helper methods for common stat operations
+func (s *Statistics) GetValue(statName string) int {
+ return s.Get(statName).Value
+}
+
+func (s *Statistics) GetValueAdj(statName string) int {
+ return s.Get(statName).ValueAdj
+}
+
+func (s *Statistics) GetBase(statName string) int {
+ return s.Get(statName).Base
+}
+
+func (s *Statistics) SetBase(statName string, value int) {
+ s.Get(statName).Base = value
+}
+
+func (s *Statistics) AddMod(statName string, value int) {
+ s.Get(statName).Mods += value
+}
+
+func (s *Statistics) SetMod(statName string, value int) {
+ s.Get(statName).Mods = value
+}
+
+func (s *Statistics) GetTraining(statName string) int {
+ return s.Get(statName).Training
+}
+
+func (s *Statistics) SetTraining(statName string, value int) {
+ s.Get(statName).Training = value
+}
diff --git a/internal/templates/templatesfunctions.go b/internal/templates/templatesfunctions.go
index 0a4fe1f4..c70bf5fe 100644
--- a/internal/templates/templatesfunctions.go
+++ b/internal/templates/templatesfunctions.go
@@ -23,6 +23,24 @@ import (
var (
funcMap = template.FuncMap{
+ "charStatBase": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).Base
+ },
+ "charStatTraining": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).Training
+ },
+ "charStatMods": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).Mods
+ },
+ "charStatValue": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).Value
+ },
+ "charStatValueAdj": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).ValueAdj
+ },
+ "charStatRacial": func(char *characters.Character, name string) int {
+ return char.Stats.Get(name).Racial
+ },
"pad": pad,
"padLeft": padLeft,
"padRight": padRight,
diff --git a/internal/usercommands/experience.go b/internal/usercommands/experience.go
index fb7fe814..7970c8d2 100644
--- a/internal/usercommands/experience.go
+++ b/internal/usercommands/experience.go
@@ -90,30 +90,32 @@ func Experience(rest string, user *users.UserRecord, room *rooms.Room, flags eve
stats := []string{`str`, `spd`, `smt`, `vit`, `mys`, `per`}
+ // TODO: Need to allow for this stat list to be dynamic
oldG := map[string]int{
- `str`: mockChar.Stats.Strength.GainsForLevel(startLevel - 1),
- `spd`: mockChar.Stats.Speed.GainsForLevel(startLevel - 1),
- `smt`: mockChar.Stats.Smarts.GainsForLevel(startLevel - 1),
- `vit`: mockChar.Stats.Vitality.GainsForLevel(startLevel - 1),
- `mys`: mockChar.Stats.Mysticism.GainsForLevel(startLevel - 1),
- `per`: mockChar.Stats.Perception.GainsForLevel(startLevel - 1),
+ `str`: mockChar.Stats.Get("Strength").GainsForLevel(startLevel - 1),
+ `spd`: mockChar.Stats.Get("Speed").GainsForLevel(startLevel - 1),
+ `smt`: mockChar.Stats.Get("Smarts").GainsForLevel(startLevel - 1),
+ `vit`: mockChar.Stats.Get("Vitality").GainsForLevel(startLevel - 1),
+ `mys`: mockChar.Stats.Get("Mysticism").GainsForLevel(startLevel - 1),
+ `per`: mockChar.Stats.Get("Perception").GainsForLevel(startLevel - 1),
}
- newG := map[string]int{}
totalG := map[string]int{}
for _, stat := range stats {
totalG[stat] = oldG[stat]
}
+ // TODO: newG is never used
+ newG := map[string]int{}
for i := startLevel; i <= endLevel; i++ {
newG = map[string]int{
- `str`: mockChar.Stats.Strength.GainsForLevel(i),
- `spd`: mockChar.Stats.Speed.GainsForLevel(i),
- `smt`: mockChar.Stats.Smarts.GainsForLevel(i),
- `vit`: mockChar.Stats.Vitality.GainsForLevel(i),
- `mys`: mockChar.Stats.Mysticism.GainsForLevel(i),
- `per`: mockChar.Stats.Perception.GainsForLevel(i),
+ `str`: mockChar.Stats.Get("Strength").GainsForLevel(i),
+ `spd`: mockChar.Stats.Get("Speed").GainsForLevel(i),
+ `smt`: mockChar.Stats.Get("Smarts").GainsForLevel(i),
+ `vit`: mockChar.Stats.Get("Vitality").GainsForLevel(i),
+ `mys`: mockChar.Stats.Get("Mysticism").GainsForLevel(i),
+ `per`: mockChar.Stats.Get("Perception").GainsForLevel(i),
}
tnlXP := mockChar.XPTL(i) - mockChar.XPTL(i-1)
diff --git a/internal/usercommands/go.go b/internal/usercommands/go.go
index 5a8c1834..e8757160 100644
--- a/internal/usercommands/go.go
+++ b/internal/usercommands/go.go
@@ -288,13 +288,13 @@ func Go(rest string, user *users.UserRecord, room *rooms.Room, flags events.Even
continue
}
- speedDelta := mob.Character.Stats.Speed.ValueAdj - user.Character.Stats.Speed.ValueAdj
+ speedDelta := mob.Character.Stats.Get("Speed").ValueAdj - user.Character.Stats.Get("Speed").ValueAdj
if speedDelta < 1 {
speedDelta = 1
}
// Chance that a mob follows the player
- targetVal := 20 + mob.Character.Stats.Perception.ValueAdj + speedDelta
+ targetVal := 20 + mob.Character.Stats.Get("Perception").ValueAdj + speedDelta
roll := util.Rand(100)
diff --git a/internal/usercommands/skill.brawling.disarm.go b/internal/usercommands/skill.brawling.disarm.go
index 21602305..55160cbf 100644
--- a/internal/usercommands/skill.brawling.disarm.go
+++ b/internal/usercommands/skill.brawling.disarm.go
@@ -56,7 +56,7 @@ func Disarm(rest string, user *users.UserRecord, room *rooms.Room, flags events.
return true, nil
}
- chanceIn100 := (user.Character.Stats.Speed.ValueAdj + user.Character.Stats.Smarts.ValueAdj) - (m.Character.Stats.Strength.ValueAdj + m.Character.Stats.Perception.ValueAdj)
+ chanceIn100 := (user.Character.Stats.Get("Speed").ValueAdj + user.Character.Stats.Get("Smarts").ValueAdj) - (m.Character.Stats.Get("Strength").ValueAdj + m.Character.Stats.Get("Perception").ValueAdj)
if chanceIn100 < 0 {
chanceIn100 = 0
}
@@ -103,7 +103,7 @@ func Disarm(rest string, user *users.UserRecord, room *rooms.Room, flags events.
return true, nil
}
- chanceIn100 := (user.Character.Stats.Speed.ValueAdj + user.Character.Stats.Smarts.ValueAdj) - (u.Character.Stats.Strength.ValueAdj + u.Character.Stats.Perception.ValueAdj)
+ chanceIn100 := (user.Character.Stats.Get("Speed").ValueAdj + user.Character.Stats.Get("Smarts").ValueAdj) - (u.Character.Stats.Get("Strength").ValueAdj + u.Character.Stats.Get("Perception").ValueAdj)
if chanceIn100 < 0 {
chanceIn100 = 0
}
diff --git a/internal/usercommands/skill.brawling.tackle.go b/internal/usercommands/skill.brawling.tackle.go
index 3ae1ba4c..04e472dc 100644
--- a/internal/usercommands/skill.brawling.tackle.go
+++ b/internal/usercommands/skill.brawling.tackle.go
@@ -43,7 +43,7 @@ func Tackle(rest string, user *users.UserRecord, room *rooms.Room, flags events.
if m != nil {
- chanceIn100 := user.Character.Stats.Speed.ValueAdj - m.Character.Stats.Perception.ValueAdj
+ chanceIn100 := user.Character.Stats.Get("Speed").ValueAdj - m.Character.Stats.Get("Perception").ValueAdj
if chanceIn100 < 0 {
chanceIn100 = 0
}
@@ -90,7 +90,7 @@ func Tackle(rest string, user *users.UserRecord, room *rooms.Room, flags events.
if u != nil {
- chanceIn100 := user.Character.Stats.Speed.ValueAdj - u.Character.Stats.Perception.ValueAdj
+ chanceIn100 := user.Character.Stats.Get("Speed").ValueAdj - u.Character.Stats.Get("Perception").ValueAdj
if chanceIn100 < 0 {
chanceIn100 = 0
}
diff --git a/internal/usercommands/skill.enchant.go b/internal/usercommands/skill.enchant.go
index c1f0c4fb..e3410c8d 100644
--- a/internal/usercommands/skill.enchant.go
+++ b/internal/usercommands/skill.enchant.go
@@ -143,9 +143,9 @@ func Enchant(rest string, user *users.UserRecord, room *rooms.Room, flags events
*/
chanceToDestroy := 50
- chanceToDestroy -= skillLevel * 10 // 10-40% reduction
- chanceToDestroy += int(matchItem.Enchantments) * 20 // 20% greater chance for each enchantment.
- chanceToDestroy -= user.Character.Stats.Mysticism.ValueAdj >> 2 // 1% less chance for each 4 mysticism points
+ chanceToDestroy -= skillLevel * 10 // 10-40% reduction
+ chanceToDestroy += int(matchItem.Enchantments) * 20 // 20% greater chance for each enchantment.
+ chanceToDestroy -= user.Character.Stats.Get("Mysticism").ValueAdj >> 2 // 1% less chance for each 4 mysticism points
if onlyConsider {
user.SendText(
@@ -168,12 +168,12 @@ func Enchant(rest string, user *users.UserRecord, room *rooms.Room, flags events
// At skill level 1, can enchant only weapons
if matchItem.GetSpec().Type == items.Weapon {
- damageBonus = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Mysticism.ValueAdj))))
+ damageBonus = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Get("Mysticism").ValueAdj))))
}
// At skill level 2, can enchant weapons and armor
if skillLevel >= 2 && matchItem.GetSpec().Subtype == items.Wearable {
- defenseBonus = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Mysticism.ValueAdj))))
+ defenseBonus = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Get("Mysticism").ValueAdj))))
}
// At skill level 3, can provide stat bonuses
@@ -184,7 +184,7 @@ func Enchant(rest string, user *users.UserRecord, room *rooms.Room, flags events
for i := 0; i < bonusCt; i++ {
// select a random stat
chosenStat := allStats[util.Rand(len(allStats))]
- statBonus[chosenStat] = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Mysticism.ValueAdj))))
+ statBonus[chosenStat] = int(math.Ceil(math.Sqrt(float64(user.Character.Stats.Get("Mysticism").ValueAdj))))
}
}
diff --git a/internal/usercommands/skill.portal.go b/internal/usercommands/skill.portal.go
index 7e0d3593..920eb420 100644
--- a/internal/usercommands/skill.portal.go
+++ b/internal/usercommands/skill.portal.go
@@ -69,7 +69,7 @@ func Portal(rest string, user *users.UserRecord, room *rooms.Room, flags events.
}
}
- portalLifeInSeconds := user.Character.Stats.Mysticism.ValueAdj * 10 // 0 mysticism = 30 seconds, 100 mysticism = 1030 seconds
+ portalLifeInSeconds := user.Character.Stats.Get("Mysticism").ValueAdj * 10 // 0 mysticism = 30 seconds, 100 mysticism = 1030 seconds
if portalLifeInSeconds < 0 {
portalLifeInSeconds = 0
}
diff --git a/internal/usercommands/skill.protection.pray.go b/internal/usercommands/skill.protection.pray.go
index 4fe29ca1..dc2bdffe 100644
--- a/internal/usercommands/skill.protection.pray.go
+++ b/internal/usercommands/skill.protection.pray.go
@@ -46,7 +46,7 @@ func Pray(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
}
possibleBuffIds := []int{4, 11, 14, 16, 17, 18}
- totalBuffCount := 1 + int(float64(user.Character.Stats.Mysticism.ValueAdj)/15) + util.Rand(2)
+ totalBuffCount := 1 + int(float64(user.Character.Stats.Get("Mysticism").ValueAdj)/15) + util.Rand(2)
if totalBuffCount > len(possibleBuffIds) {
totalBuffCount = len(possibleBuffIds)
diff --git a/internal/usercommands/skill.search.go b/internal/usercommands/skill.search.go
index 993dcd3f..060b4b8b 100644
--- a/internal/usercommands/skill.search.go
+++ b/internal/usercommands/skill.search.go
@@ -44,7 +44,7 @@ func Search(rest string, user *users.UserRecord, room *rooms.Room, flags events.
}
// 10% + 1% for every 2 smarts
- searchOddsIn100 := 10 + int(math.Ceil(float64(user.Character.Stats.Perception.ValueAdj)/2))
+ searchOddsIn100 := 10 + int(math.Ceil(float64(user.Character.Stats.Get("Perception").ValueAdj)/2))
user.SendText("You snoop around for a bit...\n")
room.SendText(
diff --git a/internal/usercommands/skill.skulduggery.bump.go b/internal/usercommands/skill.skulduggery.bump.go
index b7714581..a4aa70f9 100644
--- a/internal/usercommands/skill.skulduggery.bump.go
+++ b/internal/usercommands/skill.skulduggery.bump.go
@@ -68,7 +68,7 @@ func Bump(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
levelDelta = 1
}
- chanceIn100 := user.Character.Stats.Strength.ValueAdj / 2
+ chanceIn100 := user.Character.Stats.Get("Strength").ValueAdj / 2
chanceIn100 /= levelDelta
if chanceIn100 < 0 {
chanceIn100 = 1
@@ -113,7 +113,7 @@ func Bump(rest string, user *users.UserRecord, room *rooms.Room, flags events.Ev
if levelDelta < 1 {
levelDelta = 1
}
- chanceIn100 := user.Character.Stats.Strength.ValueAdj / 2
+ chanceIn100 := user.Character.Stats.Get("Strength").ValueAdj / 2
chanceIn100 /= levelDelta
if chanceIn100 < 0 {
chanceIn100 = 1
diff --git a/internal/usercommands/skill.skulduggery.pickpocket.go b/internal/usercommands/skill.skulduggery.pickpocket.go
index 12dc4efb..26095bfb 100644
--- a/internal/usercommands/skill.skulduggery.pickpocket.go
+++ b/internal/usercommands/skill.skulduggery.pickpocket.go
@@ -63,7 +63,7 @@ func Pickpocket(rest string, user *users.UserRecord, room *rooms.Room, flags eve
levelDelta = 1
}
- chanceIn100 := (user.Character.Stats.Speed.ValueAdj+user.Character.Stats.Smarts.ValueAdj+user.Character.Stats.Perception.ValueAdj)/3 - m.Character.Stats.Perception.ValueAdj
+ chanceIn100 := (user.Character.Stats.Get("Speed").ValueAdj+user.Character.Stats.Get("Smarts").ValueAdj+user.Character.Stats.Get("Perception").ValueAdj)/3 - m.Character.Stats.Get("Perception").ValueAdj
chanceIn100 /= levelDelta
if chanceIn100 < 0 {
chanceIn100 = 1
@@ -160,7 +160,7 @@ func Pickpocket(rest string, user *users.UserRecord, room *rooms.Room, flags eve
levelDelta = 1
}
- chanceIn100 := (user.Character.Stats.Speed.ValueAdj+user.Character.Stats.Smarts.ValueAdj+user.Character.Stats.Perception.ValueAdj)/3 - p.Character.Stats.Perception.ValueAdj
+ chanceIn100 := (user.Character.Stats.Get("Speed").ValueAdj+user.Character.Stats.Get("Smarts").ValueAdj+user.Character.Stats.Get("Perception").ValueAdj)/3 - p.Character.Stats.Get("Perception").ValueAdj
chanceIn100 /= levelDelta
if chanceIn100 < 0 {
chanceIn100 = 1
diff --git a/internal/usercommands/status.go b/internal/usercommands/status.go
index c1447fda..6c5476f9 100644
--- a/internal/usercommands/status.go
+++ b/internal/usercommands/status.go
@@ -14,7 +14,7 @@ import (
func Status(rest string, user *users.UserRecord, room *rooms.Room, flags events.EventFlag) (bool, error) {
- //possibleStatuses := []string{`strength`, `speed`, `smarts`, `vitality`, `mysticism`, `perception`}
+ statNames := user.Character.Stats.GetStatInfoNames()
if rest != `` {
@@ -32,7 +32,7 @@ func Status(rest string, user *users.UserRecord, room *rooms.Room, flags events.
user.SendText(tplTxt)
}
- question := cmdPrompt.Ask(`Increase which?`, []string{`strength`, `speed`, `smarts`, `vitality`, `mysticism`, `perception`, `quit`}, `quit`)
+ question := cmdPrompt.Ask(`Increase which?`, append(statNames, `quit`), `quit`)
if !question.Done {
return true, nil
}
@@ -42,7 +42,7 @@ func Status(rest string, user *users.UserRecord, room *rooms.Room, flags events.
return true, nil
}
- match, closeMatch := util.FindMatchIn(question.Response, []string{`strength`, `speed`, `smarts`, `vitality`, `mysticism`, `perception`}...)
+ match, closeMatch := util.FindMatchIn(question.Response, statNames...)
question.RejectResponse() // Always reset this question, since we want to keep reusing it.
@@ -60,32 +60,10 @@ func Status(rest string, user *users.UserRecord, room *rooms.Room, flags events.
after := 0
spent := 0
- switch selection {
- case `strength`:
- before = user.Character.Stats.Strength.Value - user.Character.Stats.Strength.Mods
- user.Character.Stats.Strength.Training += 1
- spent = 1
- case `speed`:
- before = user.Character.Stats.Speed.Value - user.Character.Stats.Speed.Mods
- user.Character.Stats.Speed.Training += 1
- spent = 1
- case `smarts`:
- before = user.Character.Stats.Smarts.Value - user.Character.Stats.Smarts.Mods
- user.Character.Stats.Smarts.Training += 1
- spent = 1
- case `vitality`:
- before = user.Character.Stats.Vitality.Value - user.Character.Stats.Vitality.Mods
- user.Character.Stats.Vitality.Training += 1
- spent = 1
- case `mysticism`:
- before = user.Character.Stats.Mysticism.Value - user.Character.Stats.Mysticism.Mods
- user.Character.Stats.Mysticism.Training += 1
- spent = 1
- case `perception`:
- before = user.Character.Stats.Perception.Value - user.Character.Stats.Perception.Mods
- user.Character.Stats.Perception.Training += 1
- spent = 1
- }
+ // TODO: Now that we have removed the switch statement and replaced it with a map this command will always succeed so spent = 1 will always be true if the character has training points to spend. Need to come back to this and add some checks to see if the stat can actually be trained.
+ before = user.Character.Stats.Get(selection).Value - user.Character.Stats.Get(selection).Mods
+ user.Character.Stats.Get(selection).Training += 1
+ spent = 1
if spent > 0 {
after = before + 1
diff --git a/internal/users/userrecord.go b/internal/users/userrecord.go
index f98d36ef..002c98f4 100644
--- a/internal/users/userrecord.go
+++ b/internal/users/userrecord.go
@@ -218,12 +218,12 @@ func (u *UserRecord) GrantXP(amt int, source string) {
u.EventLog.Add(`xp`, fmt.Sprintf(`%s is now level %d !`, u.Character.Name, u.Character.Level))
levelUpEvent.LevelsGained += 1
- levelUpEvent.StatsDelta.Strength.Value += statsDelta.Strength.Value
- levelUpEvent.StatsDelta.Speed.Value += statsDelta.Speed.Value
- levelUpEvent.StatsDelta.Smarts.Value += statsDelta.Smarts.Value
- levelUpEvent.StatsDelta.Vitality.Value += statsDelta.Vitality.Value
- levelUpEvent.StatsDelta.Mysticism.Value += statsDelta.Mysticism.Value
- levelUpEvent.StatsDelta.Perception.Value += statsDelta.Perception.Value
+ levelUpEvent.StatsDelta.Get("Strength").Value += statsDelta.Get("Strength").Value
+ levelUpEvent.StatsDelta.Get("Speed").Value += statsDelta.Get("Speed").Value
+ levelUpEvent.StatsDelta.Get("Smarts").Value += statsDelta.Get("Smarts").Value
+ levelUpEvent.StatsDelta.Get("Vitality").Value += statsDelta.Get("Vitality").Value
+ levelUpEvent.StatsDelta.Get("Mysticism").Value += statsDelta.Get("Mysticism").Value
+ levelUpEvent.StatsDelta.Get("Perception").Value += statsDelta.Get("Perception").Value
levelUpEvent.TrainingPoints += 1
levelUpEvent.StatPoints += 1
diff --git a/modules/gmcp/gmcp.Char.go b/modules/gmcp/gmcp.Char.go
index 8ca87055..15017123 100644
--- a/modules/gmcp/gmcp.Char.go
+++ b/modules/gmcp/gmcp.Char.go
@@ -475,12 +475,12 @@ func (g *GMCPCharModule) GetCharNode(user *users.UserRecord, gmcpModule string)
if all || g.wantsGMCPPayload(`Char.Stats`, gmcpModule) {
payload.Stats = &GMCPCharModule_Payload_Stats{
- Strength: user.Character.Stats.Strength.ValueAdj,
- Speed: user.Character.Stats.Speed.ValueAdj,
- Smarts: user.Character.Stats.Smarts.ValueAdj,
- Vitality: user.Character.Stats.Vitality.ValueAdj,
- Mysticism: user.Character.Stats.Mysticism.ValueAdj,
- Perception: user.Character.Stats.Perception.ValueAdj,
+ Strength: user.Character.Stats.GetValueAdj("Strength"),
+ Speed: user.Character.Stats.GetValueAdj("Speed"),
+ Smarts: user.Character.Stats.GetValueAdj("Smarts"),
+ Vitality: user.Character.Stats.GetValueAdj("Vitality"),
+ Mysticism: user.Character.Stats.GetValueAdj("Mysticism"),
+ Perception: user.Character.Stats.GetValueAdj("Perception"),
}
if !all {