diff --git a/build.sh b/build.sh index 6cd2aa14f..feffc26d9 100755 --- a/build.sh +++ b/build.sh @@ -56,6 +56,11 @@ for arg in "$@"; do BUILD_GEN=1 SOMETHING=1 ;; + --win | -w) + EXTRA_LD_FLAGS="-s -w" + BUILD_GO=1 + SOMETHING=1 + ;; --help | -h) echo "$0 [options]" echo " -a, --all Equivalent to --gen --go --lint --race" @@ -67,6 +72,7 @@ for arg in "$@"; do echo " -l, --lint Run the linters" echo " -r, --race Run the tests with race-checking enabled" echo " -t, --test Run the tests" + echo " -w, --win Force Build for Windows" echo " -h, --help This help text" exit 0 ;; diff --git a/model/gurps/sheet_settings.go b/model/gurps/sheet_settings.go index bfb7d4561..342bf553f 100644 --- a/model/gurps/sheet_settings.go +++ b/model/gurps/sheet_settings.go @@ -54,6 +54,7 @@ type SheetSettingsData struct { ExcludeUnspentPointsFromTotal bool `json:"exclude_unspent_points_from_total,omitempty"` ShowLiftingSTDamage bool `json:"show_lifting_st_damage,omitempty"` ShowIQBasedDamage bool `json:"show_iq_based_damage,omitempty"` + UseSkillTrees bool `json:"use_skill_trees,omitempty"` } // SheetSettings holds sheet settings. diff --git a/model/gurps/skill.go b/model/gurps/skill.go index fd8391054..6accfc7ff 100644 --- a/model/gurps/skill.go +++ b/model/gurps/skill.go @@ -525,10 +525,11 @@ func (s *Skill) DefaultSkill() *Skill { if e == nil { return nil } + requirePoints := !e.SheetSettings.UseSkillTrees if s.IsTechnique() { - return s.BaseSkill(e, s.TechniqueDefault, true) + return s.BaseSkill(e, s.TechniqueDefault, requirePoints) } - return s.BaseSkill(e, s.DefaultedFrom, true) + return s.BaseSkill(e, s.DefaultedFrom, requirePoints) } // HasDefaultTo returns true if the set of possible defaults includes the other skill. @@ -728,8 +729,13 @@ func (s *Skill) DecrementSkillLevel() { func (s *Skill) CalculateLevel(excludes map[string]bool) Level { points := s.AdjustedPoints(nil) if s.IsTechnique() { - return CalculateTechniqueLevel(EntityFromNode(s), s.Replacements, s.NameWithReplacements(), - s.SpecializationWithReplacements(), s.Tags, s.TechniqueDefault, s.Difficulty.Difficulty, points, true, + e := EntityFromNode(s) + requirePoints := true + if e != nil { + requirePoints = !e.SheetSettings.UseSkillTrees + } + return CalculateTechniqueLevel(e, s.Replacements, s.NameWithReplacements(), + s.SpecializationWithReplacements(), s.Tags, s.TechniqueDefault, s.Difficulty.Difficulty, points, requirePoints, s.TechniqueLimitModifier, excludes) } return CalculateSkillLevel(EntityFromNode(s), s.NameWithReplacements(), s.SpecializationWithReplacements(), s.Tags, @@ -826,7 +832,7 @@ func CalculateTechniqueLevel(e *Entity, replacements map[string]string, name, sp } } else { // Take the modifier back out, as we wanted the base, not the final value. - level = def.SkillLevelFast(e, replacements, true, nil, false) - def.Modifier + level = def.SkillLevelFast(e, replacements, requirePoints, nil, false) - def.Modifier } if level != fxp.Min { baseLevel := level @@ -889,16 +895,18 @@ func (s *Skill) bestDefaultWithPoints(excluded *SkillDefault) *SkillDefault { } func (s *Skill) bestDefault(excluded *SkillDefault) *SkillDefault { - if EntityFromNode(s) == nil || len(s.Defaults) == 0 { + e := EntityFromNode(s) + if e == nil || len(s.Defaults) == 0 { return nil } + requirePoints := !e.SheetSettings.UseSkillTrees excludes := make(map[string]bool) excludes[s.String()] = true var bestDef *SkillDefault best := fxp.Min for _, def := range s.resolveToSpecificDefaults() { // For skill-based defaults, prune out any that already use a default that we are involved with - if def.Equivalent(s.Replacements, excluded) || s.inDefaultChain(def, make(map[*Skill]bool)) { + if def.Equivalent(s.Replacements, excluded) || (s.inDefaultChain(def, make(map[*Skill]bool)) && requirePoints) { continue } if level := s.calcSkillDefaultLevel(def, excludes); best < level { @@ -912,11 +920,15 @@ func (s *Skill) bestDefault(excluded *SkillDefault) *SkillDefault { func (s *Skill) calcSkillDefaultLevel(def *SkillDefault, excludes map[string]bool) fxp.Int { e := EntityFromNode(s) - level := def.SkillLevel(e, s.Replacements, true, excludes, !s.IsTechnique()) - if def.SkillBased() { + requirePoints := true + if e != nil { + requirePoints = !e.SheetSettings.UseSkillTrees + } + level := def.SkillLevel(e, s.Replacements, requirePoints, excludes, !s.IsTechnique()) + if def.SkillBased() && e != nil { defName := def.NameWithReplacements(s.Replacements) defSpec := def.SpecializationWithReplacements(s.Replacements) - if other := e.BestSkillNamed(defName, defSpec, true, excludes); other != nil { + if other := e.BestSkillNamed(defName, defSpec, requirePoints, excludes); other != nil { level -= e.SkillBonusFor(defName, defSpec, s.Tags, nil) } } @@ -928,8 +940,9 @@ func (s *Skill) inDefaultChain(def *SkillDefault, lookedAt map[*Skill]bool) bool if e == nil || def == nil || !def.SkillBased() { return false } + requirePoints := !e.SheetSettings.UseSkillTrees for _, one := range e.SkillNamed(def.NameWithReplacements(s.Replacements), - def.SpecializationWithReplacements(s.Replacements), true, nil) { + def.SpecializationWithReplacements(s.Replacements), requirePoints, nil) { if one == s { return true } @@ -950,8 +963,9 @@ func (s *Skill) resolveToSpecificDefaults() []*SkillDefault { if e == nil || def == nil || !def.SkillBased() { result = append(result, def) } else { + requirePoints := !e.SheetSettings.UseSkillTrees for _, one := range e.SkillNamed(def.NameWithReplacements(s.Replacements), - def.SpecializationWithReplacements(s.Replacements), true, + def.SpecializationWithReplacements(s.Replacements), requirePoints, map[string]bool{s.String(): true}) { local := *def local.Name = one.NameWithReplacements() @@ -969,6 +983,9 @@ func (s *Skill) TechniqueSatisfied(tooltip *xbytes.InsertBuffer, prefix string) return true } e := EntityFromNode(s) + if e != nil && e.SheetSettings.UseSkillTrees { + return true + } sk := e.BestSkillNamed(s.TechniqueDefault.NameWithReplacements(s.Replacements), s.TechniqueDefault.SpecializationWithReplacements(s.Replacements), false, nil) satisfied := sk != nil && (sk.IsTechnique() || sk.Points > 0) @@ -1123,7 +1140,8 @@ func (s *Skill) SwapDefaults() { def := s.DefaultedFrom s.DefaultedFrom = nil if e := EntityFromNode(s); e != nil { - if baseSkill := s.BaseSkill(e, s.bestDefault(nil), true); baseSkill != nil { + requirePoints := !e.SheetSettings.UseSkillTrees + if baseSkill := s.BaseSkill(e, s.bestDefault(nil), requirePoints); baseSkill != nil { s.DefaultedFrom = s.bestDefaultWithPoints(def) baseSkill.UpdateLevel() s.UpdateLevel() diff --git a/ux/pageref_name_mappings.go b/ux/pageref_name_mappings.go index 5b51e71fe..423dfc1e7 100644 --- a/ux/pageref_name_mappings.go +++ b/ux/pageref_name_mappings.go @@ -227,6 +227,7 @@ var PageRefKeyNameMappings = map[string]string{ "PU7:": "Power-Ups 7: Wildcard Skills", "PU8:": "Power-Ups 8: Limitations", "PU9:": "Power-Ups 9: Alternate Attributes", + "PU10:": "Power-Ups 10: Skill Trees", "PW": "Powers: The Weird", "PY#:": "Pyramid 3 issues (replace # with the issue number, but leave out the leading \"3-\")", "PY4-#:": "Pyramid 4 issues (replace # with the issue number)", diff --git a/ux/sheet_settings_dockable.go b/ux/sheet_settings_dockable.go index 2794e545b..0545a2928 100644 --- a/ux/sheet_settings_dockable.go +++ b/ux/sheet_settings_dockable.go @@ -53,6 +53,7 @@ type sheetSettingsDockable struct { useHalfStatDefaults *unison.CheckBox showLiftingSTDamage *unison.CheckBox showIQBasedDamage *unison.CheckBox + useSkillTrees *unison.CheckBox lengthUnitsPopup *unison.PopupMenu[fxp.LengthUnit] weightUnitsPopup *unison.PopupMenu[fxp.WeightUnit] userDescDisplayPopup *unison.PopupMenu[display.Option] @@ -227,6 +228,11 @@ func (d *sheetSettingsDockable) createOptions(content *unison.Panel) { d.settings().ShowIQBasedDamage = d.showIQBasedDamage.State == check.On d.syncSheet(false) }) + d.useSkillTrees = d.addCheckBoxWithLink(panel, i18n.Text("Use Skill Trees"), "PU10:4", + s.UseSkillTrees, func() { + d.settings().UseSkillTrees = d.useSkillTrees.State == check.On + d.syncSheet(false) + }) content.AddChild(panel) } @@ -488,6 +494,7 @@ func (d *sheetSettingsDockable) sync() { d.showTitleInsteadOfNameInPageFooter.State = check.FromBool(s.UseTitleInFooter) d.showLiftingSTDamage.State = check.FromBool(s.ShowLiftingSTDamage) d.showIQBasedDamage.State = check.FromBool(s.ShowIQBasedDamage) + d.useSkillTrees.State = check.FromBool(s.UseSkillTrees) d.useMultiplicativeModifiers.State = check.FromBool(s.UseMultiplicativeModifiers) d.useHalfStatDefaults.State = check.FromBool(s.UseHalfStatDefaults) d.useModifyDicePlusAdds.State = check.FromBool(s.UseModifyingDicePlusAdds)