You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
4.5 KiB
Go
177 lines
4.5 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/denisovdennis/autohero/internal/model"
|
|
)
|
|
|
|
type ContentStore struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewContentStore(pool *pgxpool.Pool) *ContentStore {
|
|
return &ContentStore{pool: pool}
|
|
}
|
|
|
|
func (s *ContentStore) LoadEnemyTemplates(ctx context.Context) (map[model.EnemyType]model.Enemy, error) {
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT type, name, hp, max_hp, attack, defense, speed, crit_chance,
|
|
min_level, max_level, xp_reward, gold_reward, special_abilities, is_elite
|
|
FROM enemies
|
|
`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load enemies from db: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
out := make(map[model.EnemyType]model.Enemy)
|
|
for rows.Next() {
|
|
var (
|
|
t string
|
|
e model.Enemy
|
|
specialAbilities []string
|
|
)
|
|
if err := rows.Scan(
|
|
&t, &e.Name, &e.HP, &e.MaxHP, &e.Attack, &e.Defense, &e.Speed, &e.CritChance,
|
|
&e.MinLevel, &e.MaxLevel, &e.XPReward, &e.GoldReward, &specialAbilities, &e.IsElite,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan enemy row: %w", err)
|
|
}
|
|
e.Type = model.EnemyType(t)
|
|
e.SpecialAbilities = make([]model.SpecialAbility, 0, len(specialAbilities))
|
|
for _, a := range specialAbilities {
|
|
e.SpecialAbilities = append(e.SpecialAbilities, model.SpecialAbility(a))
|
|
}
|
|
out[e.Type] = e
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("enemy rows: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func normalizeEquipmentSlot(raw string) model.EquipmentSlot {
|
|
v := strings.TrimSpace(strings.ToLower(raw))
|
|
v = strings.TrimPrefix(v, "gear.slot.")
|
|
switch v {
|
|
case "weapon", "mainhand", "main_hand":
|
|
return model.SlotMainHand
|
|
case "armor", "chest":
|
|
return model.SlotChest
|
|
case "head":
|
|
return model.SlotHead
|
|
case "feet":
|
|
return model.SlotFeet
|
|
case "neck":
|
|
return model.SlotNeck
|
|
case "hands":
|
|
return model.SlotHands
|
|
case "legs":
|
|
return model.SlotLegs
|
|
case "cloak":
|
|
return model.SlotCloak
|
|
case "finger", "ring":
|
|
return model.SlotFinger
|
|
case "wrist":
|
|
return model.SlotWrist
|
|
default:
|
|
return model.EquipmentSlot(v)
|
|
}
|
|
}
|
|
|
|
func (s *ContentStore) LoadGearFamilies(ctx context.Context) ([]model.GearFamily, error) {
|
|
out := make([]model.GearFamily, 0, 128)
|
|
|
|
weaponRows, err := s.pool.Query(ctx, `
|
|
SELECT name, type, damage, speed, crit_chance, special_effect
|
|
FROM weapons
|
|
`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load weapons from db: %w", err)
|
|
}
|
|
for weaponRows.Next() {
|
|
var name, typ, special string
|
|
var damage int
|
|
var speed, crit float64
|
|
if err := weaponRows.Scan(&name, &typ, &damage, &speed, &crit, &special); err != nil {
|
|
weaponRows.Close()
|
|
return nil, fmt.Errorf("scan weapon row: %w", err)
|
|
}
|
|
out = append(out, model.GearFamily{
|
|
Slot: model.SlotMainHand,
|
|
FormID: "gear.form.main_hand." + typ,
|
|
Name: name,
|
|
Subtype: typ,
|
|
BasePrimary: damage,
|
|
StatType: "attack",
|
|
SpeedModifier: speed,
|
|
BaseCrit: crit,
|
|
SpecialEffect: special,
|
|
})
|
|
}
|
|
weaponRows.Close()
|
|
|
|
armorRows, err := s.pool.Query(ctx, `
|
|
SELECT name, type, defense, speed_modifier, agility_bonus, set_name, special_effect
|
|
FROM armor
|
|
`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load armor from db: %w", err)
|
|
}
|
|
for armorRows.Next() {
|
|
var name, typ, setName, special string
|
|
var defense, agi int
|
|
var speed float64
|
|
if err := armorRows.Scan(&name, &typ, &defense, &speed, &agi, &setName, &special); err != nil {
|
|
armorRows.Close()
|
|
return nil, fmt.Errorf("scan armor row: %w", err)
|
|
}
|
|
out = append(out, model.GearFamily{
|
|
Slot: model.SlotChest,
|
|
FormID: "gear.form.chest." + typ,
|
|
Name: name,
|
|
Subtype: typ,
|
|
BasePrimary: defense,
|
|
StatType: "defense",
|
|
SpeedModifier: speed,
|
|
AgilityBonus: agi,
|
|
SetName: setName,
|
|
SpecialEffect: special,
|
|
})
|
|
}
|
|
armorRows.Close()
|
|
|
|
eqRows, err := s.pool.Query(ctx, `
|
|
SELECT slot, form_id, name, base_primary, stat_type
|
|
FROM equipment_items
|
|
`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("load equipment_items from db: %w", err)
|
|
}
|
|
for eqRows.Next() {
|
|
var slot, formID, name, statType string
|
|
var basePrimary int
|
|
if err := eqRows.Scan(&slot, &formID, &name, &basePrimary, &statType); err != nil {
|
|
eqRows.Close()
|
|
return nil, fmt.Errorf("scan equipment_item row: %w", err)
|
|
}
|
|
out = append(out, model.GearFamily{
|
|
Slot: normalizeEquipmentSlot(slot),
|
|
FormID: formID,
|
|
Name: name,
|
|
BasePrimary: basePrimary,
|
|
StatType: statType,
|
|
SpeedModifier: 1.0,
|
|
})
|
|
}
|
|
eqRows.Close()
|
|
|
|
return out, nil
|
|
}
|
|
|