aboutsummaryrefslogtreecommitdiff
path: root/internal/game/science.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-19 03:05:46 -0400
committerhistoria <[not public]>2026-06-19 03:05:46 -0400
commitfbd1c30d7b9777c9df6653f0c393afa7a7dfa519 (patch)
tree7d4dd5b1fb1a5f868a330caf009ac8dc4e583025 /internal/game/science.go
parentdea4a06764c507699cf9adcd1fd0a572d227aff0 (diff)
downloadthehouseoficarus-fbd1c30d7b9777c9df6653f0c393afa7a7dfa519.tar.gz
feat: science skill roughly implemented
Diffstat (limited to 'internal/game/science.go')
-rw-r--r--internal/game/science.go332
1 files changed, 332 insertions, 0 deletions
diff --git a/internal/game/science.go b/internal/game/science.go
new file mode 100644
index 0000000..38cb138
--- /dev/null
+++ b/internal/game/science.go
@@ -0,0 +1,332 @@
+package game
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "thehouseoficarus/internal/object"
+ "thehouseoficarus/internal/player"
+)
+
+type ModCategory string
+
+const (
+ ModCombat ModCategory = "combat"
+ ModUtility ModCategory = "utility"
+ ModEnchant ModCategory = "enchant"
+ ModProcessing ModCategory = "processing"
+ ModTransport ModCategory = "transport"
+)
+
+type ModDef struct {
+ ID string
+ Name string
+ Level int
+ MaxHit int
+ BaseXP float64
+ JunkCost map[string]int
+ Category ModCategory
+ Element string
+ TargetType string
+ Destination int
+}
+
+var AllMods []*ModDef
+
+var modByID map[string]*ModDef
+
+func GetMod(id string) *ModDef {
+ return modByID[id]
+}
+
+func FindMod(input string) *ModDef {
+ if m, ok := modByID[input]; ok {
+ return m
+ }
+ lower := strings.ToLower(strings.ReplaceAll(input, " ", "_"))
+ for _, m := range AllMods {
+ if strings.HasPrefix(m.ID, lower) {
+ return m
+ }
+ }
+ lowerSpace := strings.ToLower(input)
+ for _, m := range AllMods {
+ if strings.HasPrefix(strings.ToLower(m.Name), lowerSpace) {
+ return m
+ }
+ }
+ return nil
+}
+
+type chipEntry struct {
+ Input string
+ Output string
+ Qty int
+}
+
+var enchantMap = map[string]map[string]string{
+ "enchant_1": {
+ "sapphire_ring": "ring_of_recoil",
+ "sapphire_necklace": "necklace_of_passage",
+ "sapphire_bracelet": "bracelet_of_clay",
+ },
+ "enchant_2": {
+ "emerald_ring": "ring_of_dueling",
+ "emerald_necklace": "binding_necklace",
+ "emerald_bracelet": "bracelet_of_slaughter",
+ },
+ "enchant_3": {
+ "ruby_ring": "ring_of_forging",
+ "ruby_necklace": "digsite_pendant",
+ "ruby_bracelet": "inoculation_bracelet",
+ },
+ "enchant_4": {
+ "diamond_ring": "ring_of_life",
+ "diamond_necklace": "phoenix_necklace",
+ "diamond_bracelet": "abyssal_bracelet",
+ },
+}
+
+var chipMap = map[string]chipEntry{
+ "chip_sapphire": {"sapphire_bolts", "sapphire_bolts_e", 10},
+ "chip_emerald": {"emerald_bolts", "emerald_bolts_e", 10},
+ "chip_ruby": {"ruby_bolts", "ruby_bolts_e", 10},
+ "chip_diamond": {"diamond_bolts", "diamond_bolts_e", 10},
+}
+
+func (g *Game) hasDeckEquipped(p *player.Player) bool {
+ itemID, ok := p.Equipment[object.SlotMainHand]
+ if !ok {
+ return false
+ }
+ def, err := g.ItemStore.Load(itemID)
+ if err != nil {
+ return false
+ }
+ return def.WeaponType == object.WeaponScience
+}
+
+func (g *Game) equippedProvidesJunk(p *player.Player) string {
+ itemID, ok := p.Equipment[object.SlotMainHand]
+ if !ok {
+ return ""
+ }
+ def, err := g.ItemStore.Load(itemID)
+ if err != nil {
+ return ""
+ }
+ return def.ProvidesJunk
+}
+
+func (g *Game) effectiveJunkCost(p *player.Player, mod *ModDef) map[string]int {
+ cost := make(map[string]int)
+ for k, v := range mod.JunkCost {
+ cost[k] = v
+ }
+
+ hasDeck := g.hasDeckEquipped(p)
+
+ if hasDeck {
+ delete(cost, "scrap_metal")
+ }
+
+ providesJunk := g.equippedProvidesJunk(p)
+ if providesJunk != "" {
+ delete(cost, providesJunk)
+ }
+
+ return cost
+}
+
+func (g *Game) hasJunkCost(p *player.Player, mod *ModDef) bool {
+ cost := g.effectiveJunkCost(p, mod)
+ for itemID, qty := range cost {
+ if p.CountItem(itemID) < qty {
+ return false
+ }
+ }
+ return true
+}
+
+func (g *Game) consumeJunkCost(p *player.Player, mod *ModDef) bool {
+ cost := g.effectiveJunkCost(p, mod)
+ for itemID, qty := range cost {
+ if !p.RemoveItem(itemID, qty) {
+ return false
+ }
+ }
+ g.AccountStore.SaveCharacter(p)
+ return true
+}
+
+func (g *Game) junkCostString(p *player.Player, mod *ModDef) string {
+ cost := g.effectiveJunkCost(p, mod)
+ if len(cost) == 0 {
+ return "free"
+ }
+ var parts []string
+ for itemID, qty := range cost {
+ def, _ := g.ItemStore.Load(itemID)
+ name := itemID
+ if def != nil {
+ name = def.Name
+ }
+ if qty > 1 {
+ parts = append(parts, fmt.Sprintf("%d %s", qty, name))
+ } else {
+ parts = append(parts, name)
+ }
+ }
+ sort.Strings(parts)
+ return strings.Join(parts, ", ")
+}
+
+func (g *Game) totalEquipScienceAttack(p *player.Player) int {
+ total := 0
+ for _, itemID := range p.Equipment {
+ def, err := g.ItemStore.Load(itemID)
+ if err == nil {
+ total += def.Stats.ScienceAttack
+ }
+ }
+ return total
+}
+
+func init() {
+ AllMods = []*ModDef{
+ {ID: "bio_strike", Name: "Bio Strike", Level: 1, MaxHit: 4, BaseXP: 5.5,
+ JunkCost: map[string]int{"biojunk": 2, "scrap_metal": 1},
+ Category: ModCombat, Element: "bio", TargetType: "mob"},
+ {ID: "bio_bolt", Name: "Bio Bolt", Level: 17, MaxHit: 9, BaseXP: 13.5,
+ JunkCost: map[string]int{"biojunk": 2, "chaosjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "bio", TargetType: "mob"},
+ {ID: "bio_blast", Name: "Bio Blast", Level: 41, MaxHit: 13, BaseXP: 25.5,
+ JunkCost: map[string]int{"biojunk": 3, "chaosjunk": 1, "deathjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "bio", TargetType: "mob"},
+ {ID: "bio_wave", Name: "Bio Wave", Level: 62, MaxHit: 17, BaseXP: 36.0,
+ JunkCost: map[string]int{"biojunk": 5, "deathjunk": 1, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "bio", TargetType: "mob"},
+ {ID: "bio_surge", Name: "Bio Surge", Level: 81, MaxHit: 21, BaseXP: 44.0,
+ JunkCost: map[string]int{"biojunk": 7, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "bio", TargetType: "mob"},
+
+ {ID: "hydro_strike", Name: "Hydro Strike", Level: 5, MaxHit: 6, BaseXP: 7.5,
+ JunkCost: map[string]int{"hydrojunk": 3, "ecojunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "hydro", TargetType: "mob"},
+ {ID: "hydro_bolt", Name: "Hydro Bolt", Level: 23, MaxHit: 10, BaseXP: 16.5,
+ JunkCost: map[string]int{"hydrojunk": 3, "ecojunk": 2, "scrap_metal": 1},
+ Category: ModCombat, Element: "hydro", TargetType: "mob"},
+ {ID: "hydro_blast", Name: "Hydro Blast", Level: 47, MaxHit: 14, BaseXP: 28.5,
+ JunkCost: map[string]int{"hydrojunk": 5, "ecojunk": 3, "chaosjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "hydro", TargetType: "mob"},
+ {ID: "hydro_wave", Name: "Hydro Wave", Level: 65, MaxHit: 18, BaseXP: 37.5,
+ JunkCost: map[string]int{"hydrojunk": 7, "ecojunk": 5, "deathjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "hydro", TargetType: "mob"},
+ {ID: "hydro_surge", Name: "Hydro Surge", Level: 85, MaxHit: 22, BaseXP: 46.0,
+ JunkCost: map[string]int{"hydrojunk": 10, "ecojunk": 7, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "hydro", TargetType: "mob"},
+
+ {ID: "eco_strike", Name: "Eco Strike", Level: 9, MaxHit: 7, BaseXP: 9.5,
+ JunkCost: map[string]int{"ecojunk": 2, "biojunk": 2, "scrap_metal": 1},
+ Category: ModCombat, Element: "eco", TargetType: "mob"},
+ {ID: "eco_bolt", Name: "Eco Bolt", Level: 29, MaxHit: 11, BaseXP: 19.5,
+ JunkCost: map[string]int{"ecojunk": 3, "biojunk": 2, "scrap_metal": 1},
+ Category: ModCombat, Element: "eco", TargetType: "mob"},
+ {ID: "eco_blast", Name: "Eco Blast", Level: 53, MaxHit: 15, BaseXP: 31.5,
+ JunkCost: map[string]int{"ecojunk": 4, "biojunk": 3, "chaosjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "eco", TargetType: "mob"},
+ {ID: "eco_wave", Name: "Eco Wave", Level: 70, MaxHit: 19, BaseXP: 40.0,
+ JunkCost: map[string]int{"ecojunk": 7, "biojunk": 5, "deathjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "eco", TargetType: "mob"},
+ {ID: "eco_surge", Name: "Eco Surge", Level: 90, MaxHit: 23, BaseXP: 48.5,
+ JunkCost: map[string]int{"ecojunk": 10, "biojunk": 7, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "eco", TargetType: "mob"},
+
+ {ID: "solar_strike", Name: "Solar Strike", Level: 13, MaxHit: 8, BaseXP: 11.5,
+ JunkCost: map[string]int{"solarjunk": 3, "ecojunk": 2, "scrap_metal": 1},
+ Category: ModCombat, Element: "solar", TargetType: "mob"},
+ {ID: "solar_bolt", Name: "Solar Bolt", Level: 35, MaxHit: 12, BaseXP: 22.5,
+ JunkCost: map[string]int{"solarjunk": 4, "ecojunk": 3, "scrap_metal": 1},
+ Category: ModCombat, Element: "solar", TargetType: "mob"},
+ {ID: "solar_blast", Name: "Solar Blast", Level: 59, MaxHit: 16, BaseXP: 34.5,
+ JunkCost: map[string]int{"solarjunk": 5, "ecojunk": 4, "chaosjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "solar", TargetType: "mob"},
+ {ID: "solar_wave", Name: "Solar Wave", Level: 75, MaxHit: 20, BaseXP: 42.5,
+ JunkCost: map[string]int{"solarjunk": 7, "ecojunk": 5, "deathjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "solar", TargetType: "mob"},
+ {ID: "solar_surge", Name: "Solar Surge", Level: 95, MaxHit: 24, BaseXP: 51.0,
+ JunkCost: map[string]int{"solarjunk": 10, "ecojunk": 7, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModCombat, Element: "solar", TargetType: "mob"},
+
+ {ID: "low_process", Name: "Low Level Processing", Level: 21, MaxHit: 0, BaseXP: 31.0,
+ JunkCost: map[string]int{"naturejunk": 3, "solarjunk": 1, "scrap_metal": 1},
+ Category: ModProcessing, Element: "", TargetType: "inventory"},
+ {ID: "high_process", Name: "High Level Processing", Level: 55, MaxHit: 0, BaseXP: 65.0,
+ JunkCost: map[string]int{"naturejunk": 5, "solarjunk": 1, "scrap_metal": 1},
+ Category: ModProcessing, Element: "", TargetType: "inventory"},
+
+ {ID: "bones_to_nutrients", Name: "Bones to Nutrients", Level: 15, MaxHit: 0, BaseXP: 25.0,
+ JunkCost: map[string]int{"naturejunk": 2, "ecojunk": 2, "scrap_metal": 1},
+ Category: ModUtility, Element: "", TargetType: "self"},
+ {ID: "em_grab", Name: "Electromagnetic Grab", Level: 33, MaxHit: 0, BaseXP: 43.0,
+ JunkCost: map[string]int{"lawjunk": 1, "biojunk": 1, "scrap_metal": 1},
+ Category: ModUtility, Element: "", TargetType: "ground_item"},
+ {ID: "superheat", Name: "Superheat Item", Level: 43, MaxHit: 0, BaseXP: 53.0,
+ JunkCost: map[string]int{"naturejunk": 4, "solarjunk": 1, "scrap_metal": 1},
+ Category: ModUtility, Element: "", TargetType: "inventory"},
+
+ {ID: "transport_town", Name: "Transport: Town Square", Level: 25, MaxHit: 0, BaseXP: 27.0,
+ JunkCost: map[string]int{"lawjunk": 1, "solarjunk": 1, "biojunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 1},
+ {ID: "transport_forge", Name: "Transport: Forge", Level: 31, MaxHit: 0, BaseXP: 35.0,
+ JunkCost: map[string]int{"lawjunk": 1, "ecojunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 12},
+ {ID: "transport_mine", Name: "Transport: Mining Pit", Level: 37, MaxHit: 0, BaseXP: 40.0,
+ JunkCost: map[string]int{"lawjunk": 1, "ecojunk": 1, "solarjunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 6},
+ {ID: "transport_forest", Name: "Transport: Forest", Level: 45, MaxHit: 0, BaseXP: 48.0,
+ JunkCost: map[string]int{"lawjunk": 1, "ecojunk": 1, "biojunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 22},
+ {ID: "transport_scavenge", Name: "Transport: Scavenging Post", Level: 51, MaxHit: 0, BaseXP: 52.0,
+ JunkCost: map[string]int{"lawjunk": 1, "naturejunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 9},
+ {ID: "transport_deep_mine", Name: "Transport: Deep Mine", Level: 61, MaxHit: 0, BaseXP: 60.0,
+ JunkCost: map[string]int{"lawjunk": 2, "ecojunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 7},
+ {ID: "transport_fishing", Name: "Transport: Fishing Dock", Level: 55, MaxHit: 0, BaseXP: 56.0,
+ JunkCost: map[string]int{"lawjunk": 1, "hydrojunk": 1, "biojunk": 1, "scrap_metal": 1},
+ Category: ModTransport, Element: "", TargetType: "self", Destination: 10},
+
+ {ID: "enchant_1", Name: "Enchant Level 1", Level: 7, MaxHit: 0, BaseXP: 17.5,
+ JunkCost: map[string]int{"cosmicjunk": 1, "hydrojunk": 1, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "enchant_2", Name: "Enchant Level 2", Level: 27, MaxHit: 0, BaseXP: 37.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "biojunk": 3, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "enchant_3", Name: "Enchant Level 3", Level: 49, MaxHit: 0, BaseXP: 59.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "solarjunk": 5, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "enchant_4", Name: "Enchant Level 4", Level: 57, MaxHit: 0, BaseXP: 67.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "ecojunk": 10, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+
+ {ID: "chip_sapphire", Name: "Chip Sapphire Bolts", Level: 4, MaxHit: 0, BaseXP: 9.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "hydrojunk": 1, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "chip_emerald", Name: "Chip Emerald Bolts", Level: 27, MaxHit: 0, BaseXP: 37.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "biojunk": 3, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "chip_ruby", Name: "Chip Ruby Bolts", Level: 49, MaxHit: 0, BaseXP: 59.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "solarjunk": 5, "bloodjunk": 1, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ {ID: "chip_diamond", Name: "Chip Diamond Bolts", Level: 57, MaxHit: 0, BaseXP: 67.0,
+ JunkCost: map[string]int{"cosmicjunk": 1, "ecojunk": 10, "scrap_metal": 1},
+ Category: ModEnchant, Element: "", TargetType: "inventory"},
+ }
+
+ modByID = make(map[string]*ModDef, len(AllMods))
+ for _, m := range AllMods {
+ modByID[m.ID] = m
+ }
+}