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.
257 lines
12 KiB
Go
257 lines
12 KiB
Go
package model
|
|
|
|
import "math/rand"
|
|
|
|
// GearItem represents any equippable item across all slots.
|
|
type GearItem struct {
|
|
ID int64 `json:"id"`
|
|
Slot EquipmentSlot `json:"slot"`
|
|
FormID string `json:"formId"`
|
|
Name string `json:"name"`
|
|
Subtype string `json:"subtype"`
|
|
Rarity Rarity `json:"rarity"`
|
|
Ilvl int `json:"ilvl"`
|
|
BasePrimary int `json:"basePrimary"`
|
|
PrimaryStat int `json:"primaryStat"`
|
|
StatType string `json:"statType"`
|
|
SpeedModifier float64 `json:"speedModifier"`
|
|
CritChance float64 `json:"critChance"`
|
|
AgilityBonus int `json:"agilityBonus"`
|
|
SetName string `json:"setName,omitempty"`
|
|
SpecialEffect string `json:"specialEffect,omitempty"`
|
|
}
|
|
|
|
// GearFamily is a template for generating gear drops from the unified catalog.
|
|
type GearFamily struct {
|
|
Slot EquipmentSlot
|
|
FormID string
|
|
Name string
|
|
Subtype string // "daggers", "sword", "axe", "light", "medium", "heavy", ""
|
|
BasePrimary int
|
|
StatType string
|
|
SpeedModifier float64
|
|
BaseCrit float64
|
|
AgilityBonus int
|
|
SetName string
|
|
SpecialEffect string
|
|
}
|
|
|
|
// GearCatalog is the unified catalog of all gear families.
|
|
var GearCatalog []GearFamily
|
|
|
|
// ArmorSetBonuses maps set names to their bonus description.
|
|
var GearSetBonuses = map[string]string{
|
|
"Assassin's Set": "+crit_chance",
|
|
"Knight's Set": "+defense",
|
|
"Berserker's Set": "+attack",
|
|
"Ancient Guardian's Set": "+all_stats",
|
|
}
|
|
|
|
func init() {
|
|
// Weapons -> main_hand
|
|
for _, w := range legacyWeapons {
|
|
GearCatalog = append(GearCatalog, GearFamily{
|
|
Slot: SlotMainHand,
|
|
FormID: "gear.form.main_hand." + string(w.Type),
|
|
Name: w.Name,
|
|
Subtype: string(w.Type),
|
|
BasePrimary: w.Damage,
|
|
StatType: "attack",
|
|
SpeedModifier: w.Speed,
|
|
BaseCrit: w.CritChance,
|
|
SpecialEffect: w.SpecialEffect,
|
|
})
|
|
}
|
|
|
|
// Armors -> chest
|
|
for _, a := range legacyArmors {
|
|
GearCatalog = append(GearCatalog, GearFamily{
|
|
Slot: SlotChest,
|
|
FormID: "gear.form.chest." + string(a.Type),
|
|
Name: a.Name,
|
|
Subtype: string(a.Type),
|
|
BasePrimary: a.Defense,
|
|
StatType: "defense",
|
|
SpeedModifier: a.SpeedModifier,
|
|
AgilityBonus: a.AgilityBonus,
|
|
SetName: a.SetName,
|
|
SpecialEffect: a.SpecialEffect,
|
|
})
|
|
}
|
|
|
|
// Extended equipment families -> head, feet, neck, hands, legs, cloak, finger, wrist
|
|
for slot, families := range legacyEquipmentFamilies {
|
|
for _, f := range families {
|
|
GearCatalog = append(GearCatalog, GearFamily{
|
|
Slot: slot,
|
|
FormID: f.FormID,
|
|
Name: f.Name,
|
|
BasePrimary: f.BasePrimary,
|
|
StatType: f.StatType,
|
|
SpeedModifier: 1.0,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Build the by-slot-and-rarity index.
|
|
gearBySlot = make(map[EquipmentSlot][]GearFamily)
|
|
for _, gf := range GearCatalog {
|
|
gearBySlot[gf.Slot] = append(gearBySlot[gf.Slot], gf)
|
|
}
|
|
}
|
|
|
|
var gearBySlot map[EquipmentSlot][]GearFamily
|
|
|
|
// PickGearFamily selects a random gear family for the given slot.
|
|
// Returns nil if no families exist for the slot.
|
|
func PickGearFamily(slot EquipmentSlot) *GearFamily {
|
|
families := gearBySlot[slot]
|
|
if len(families) == 0 {
|
|
return nil
|
|
}
|
|
f := families[rand.Intn(len(families))]
|
|
return &f
|
|
}
|
|
|
|
// NewGearItem creates a GearItem from a family template with ilvl and rarity scaling applied.
|
|
func NewGearItem(family *GearFamily, ilvl int, rarity Rarity) *GearItem {
|
|
primary := ScalePrimary(family.BasePrimary, ilvl, rarity)
|
|
return &GearItem{
|
|
Slot: family.Slot,
|
|
FormID: family.FormID,
|
|
Name: family.Name,
|
|
Subtype: family.Subtype,
|
|
Rarity: rarity,
|
|
Ilvl: ilvl,
|
|
BasePrimary: family.BasePrimary,
|
|
PrimaryStat: primary,
|
|
StatType: family.StatType,
|
|
SpeedModifier: family.SpeedModifier,
|
|
CritChance: family.BaseCrit,
|
|
AgilityBonus: family.AgilityBonus,
|
|
SetName: family.SetName,
|
|
SpecialEffect: family.SpecialEffect,
|
|
}
|
|
}
|
|
|
|
// ---------- Legacy data (moved from weapon.go, armor.go, equipment.go) ----------
|
|
|
|
// legacyWeaponEntry holds weapon catalog data for building the GearCatalog.
|
|
type legacyWeaponEntry struct {
|
|
Name string
|
|
Type string
|
|
Rarity Rarity
|
|
Damage int
|
|
Speed float64
|
|
CritChance float64
|
|
SpecialEffect string
|
|
}
|
|
|
|
var legacyWeapons = []legacyWeaponEntry{
|
|
// Daggers
|
|
{Name: "Rusty Dagger", Type: "daggers", Rarity: RarityCommon, Damage: 3, Speed: 1.3, CritChance: 0.05},
|
|
{Name: "Iron Dagger", Type: "daggers", Rarity: RarityUncommon, Damage: 5, Speed: 1.3, CritChance: 0.08},
|
|
{Name: "Assassin's Blade", Type: "daggers", Rarity: RarityRare, Damage: 8, Speed: 1.35, CritChance: 0.20},
|
|
{Name: "Phantom Edge", Type: "daggers", Rarity: RarityEpic, Damage: 12, Speed: 1.4, CritChance: 0.25},
|
|
{Name: "Fang of the Void", Type: "daggers", Rarity: RarityLegendary, Damage: 18, Speed: 1.5, CritChance: 0.30},
|
|
// Swords
|
|
{Name: "Iron Sword", Type: "sword", Rarity: RarityCommon, Damage: 7, Speed: 1.0, CritChance: 0.03},
|
|
{Name: "Steel Sword", Type: "sword", Rarity: RarityUncommon, Damage: 10, Speed: 1.0, CritChance: 0.05},
|
|
{Name: "Longsword", Type: "sword", Rarity: RarityRare, Damage: 15, Speed: 1.0, CritChance: 0.08},
|
|
{Name: "Excalibur", Type: "sword", Rarity: RarityEpic, Damage: 22, Speed: 1.05, CritChance: 0.10},
|
|
{Name: "Soul Reaver", Type: "sword", Rarity: RarityLegendary, Damage: 30, Speed: 1.1, CritChance: 0.12, SpecialEffect: "lifesteal"},
|
|
// Axes
|
|
{Name: "Rusty Axe", Type: "axe", Rarity: RarityCommon, Damage: 12, Speed: 0.7, CritChance: 0.02},
|
|
{Name: "Battle Axe", Type: "axe", Rarity: RarityUncommon, Damage: 18, Speed: 0.7, CritChance: 0.04},
|
|
{Name: "War Axe", Type: "axe", Rarity: RarityRare, Damage: 25, Speed: 0.75, CritChance: 0.06},
|
|
{Name: "Infernal Axe", Type: "axe", Rarity: RarityEpic, Damage: 35, Speed: 0.75, CritChance: 0.08},
|
|
{Name: "Godslayer's Edge", Type: "axe", Rarity: RarityLegendary, Damage: 50, Speed: 0.8, CritChance: 0.10, SpecialEffect: "splash"},
|
|
}
|
|
|
|
// legacyArmorEntry holds armor catalog data for building the GearCatalog.
|
|
type legacyArmorEntry struct {
|
|
Name string
|
|
Type string
|
|
Rarity Rarity
|
|
Defense int
|
|
SpeedModifier float64
|
|
AgilityBonus int
|
|
SetName string
|
|
SpecialEffect string
|
|
}
|
|
|
|
var legacyArmors = []legacyArmorEntry{
|
|
// Light armor
|
|
{Name: "Leather Armor", Type: "light", Rarity: RarityCommon, Defense: 3, SpeedModifier: 1.05, AgilityBonus: 3},
|
|
{Name: "Ranger's Vest", Type: "light", Rarity: RarityUncommon, Defense: 5, SpeedModifier: 1.08, AgilityBonus: 5},
|
|
{Name: "Shadow Cloak", Type: "light", Rarity: RarityRare, Defense: 8, SpeedModifier: 1.10, AgilityBonus: 8, SetName: "Assassin's Set", SpecialEffect: "crit_bonus"},
|
|
{Name: "Phantom Garb", Type: "light", Rarity: RarityEpic, Defense: 12, SpeedModifier: 1.12, AgilityBonus: 12, SetName: "Assassin's Set", SpecialEffect: "crit_bonus"},
|
|
{Name: "Whisper of the Void", Type: "light", Rarity: RarityLegendary, Defense: 16, SpeedModifier: 1.15, AgilityBonus: 18, SetName: "Assassin's Set", SpecialEffect: "crit_bonus"},
|
|
// Medium armor
|
|
{Name: "Chainmail", Type: "medium", Rarity: RarityCommon, Defense: 7, SpeedModifier: 1.0},
|
|
{Name: "Reinforced Mail", Type: "medium", Rarity: RarityUncommon, Defense: 10, SpeedModifier: 1.0},
|
|
{Name: "Battle Armor", Type: "medium", Rarity: RarityRare, Defense: 15, SpeedModifier: 1.0, SetName: "Knight's Set", SpecialEffect: "def_bonus"},
|
|
{Name: "Royal Guard", Type: "medium", Rarity: RarityEpic, Defense: 22, SpeedModifier: 1.0, SetName: "Knight's Set", SpecialEffect: "def_bonus"},
|
|
{Name: "Crown of Eternity", Type: "medium", Rarity: RarityLegendary, Defense: 30, SpeedModifier: 1.0, SetName: "Knight's Set", SpecialEffect: "def_bonus"},
|
|
// Heavy armor
|
|
{Name: "Iron Plate", Type: "heavy", Rarity: RarityCommon, Defense: 14, SpeedModifier: 0.7, AgilityBonus: -3},
|
|
{Name: "Steel Plate", Type: "heavy", Rarity: RarityUncommon, Defense: 20, SpeedModifier: 0.7, AgilityBonus: -3},
|
|
{Name: "Fortress Armor", Type: "heavy", Rarity: RarityRare, Defense: 28, SpeedModifier: 0.7, AgilityBonus: -5, SetName: "Berserker's Set", SpecialEffect: "atk_bonus"},
|
|
{Name: "Dragon Scale", Type: "heavy", Rarity: RarityEpic, Defense: 38, SpeedModifier: 0.7, AgilityBonus: -5, SetName: "Berserker's Set", SpecialEffect: "atk_bonus"},
|
|
{Name: "Dragon Slayer", Type: "heavy", Rarity: RarityLegendary, Defense: 50, SpeedModifier: 0.7, AgilityBonus: -5, SetName: "Berserker's Set", SpecialEffect: "atk_bonus"},
|
|
// Ancient Guardian's Set
|
|
{Name: "Guardian's Plate", Type: "heavy", Rarity: RarityRare, Defense: 30, SpeedModifier: 0.7, AgilityBonus: 2, SetName: "Ancient Guardian's Set", SpecialEffect: "all_stats"},
|
|
{Name: "Guardian's Bastion", Type: "heavy", Rarity: RarityEpic, Defense: 42, SpeedModifier: 0.7, AgilityBonus: 4, SetName: "Ancient Guardian's Set", SpecialEffect: "all_stats"},
|
|
{Name: "Ancient Guardian's Aegis", Type: "heavy", Rarity: RarityLegendary, Defense: 55, SpeedModifier: 0.7, AgilityBonus: 6, SetName: "Ancient Guardian's Set", SpecialEffect: "all_stats"},
|
|
}
|
|
|
|
// legacyEquipmentFamily is the template used by the old equipment system.
|
|
type legacyEquipmentFamily struct {
|
|
Name string
|
|
FormID string
|
|
BasePrimary int
|
|
StatType string
|
|
}
|
|
|
|
var legacyEquipmentFamilies = map[EquipmentSlot][]legacyEquipmentFamily{
|
|
SlotHead: {
|
|
{Name: "Leather Cap", FormID: "gear.form.head.cap", BasePrimary: 2, StatType: "defense"},
|
|
{Name: "Iron Helmet", FormID: "gear.form.head.helmet", BasePrimary: 4, StatType: "defense"},
|
|
{Name: "Crown", FormID: "gear.form.head.crown", BasePrimary: 3, StatType: "mixed"},
|
|
},
|
|
SlotFeet: {
|
|
{Name: "Sandals", FormID: "gear.form.feet.sandals", BasePrimary: 1, StatType: "speed"},
|
|
{Name: "Leather Boots", FormID: "gear.form.feet.boots", BasePrimary: 3, StatType: "defense"},
|
|
{Name: "Iron Greaves", FormID: "gear.form.feet.greaves", BasePrimary: 5, StatType: "defense"},
|
|
},
|
|
SlotNeck: {
|
|
{Name: "Wooden Pendant", FormID: "gear.form.neck.pendant", BasePrimary: 2, StatType: "mixed"},
|
|
{Name: "Silver Amulet", FormID: "gear.form.neck.amulet", BasePrimary: 4, StatType: "attack"},
|
|
{Name: "Crystal Necklace", FormID: "gear.form.neck.necklace", BasePrimary: 3, StatType: "defense"},
|
|
},
|
|
SlotHands: {
|
|
{Name: "Leather Gloves", FormID: "gear.form.hands.gloves", BasePrimary: 2, StatType: "defense"},
|
|
{Name: "Iron Gauntlets", FormID: "gear.form.hands.gauntlets", BasePrimary: 4, StatType: "defense"},
|
|
{Name: "Dragonhide Gloves", FormID: "gear.form.hands.gloves", BasePrimary: 3, StatType: "mixed"},
|
|
},
|
|
SlotLegs: {
|
|
{Name: "Leather Leggings", FormID: "gear.form.legs.leggings", BasePrimary: 2, StatType: "defense"},
|
|
{Name: "Iron Greaves", FormID: "gear.form.legs.greaves", BasePrimary: 4, StatType: "defense"},
|
|
},
|
|
SlotCloak: {
|
|
{Name: "Leather Cloak", FormID: "gear.form.cloak.cloak", BasePrimary: 2, StatType: "defense"},
|
|
{Name: "Iron Cape", FormID: "gear.form.cloak.cape", BasePrimary: 4, StatType: "defense"},
|
|
{Name: "Dragonhide Cloak", FormID: "gear.form.cloak.cloak", BasePrimary: 3, StatType: "mixed"},
|
|
},
|
|
SlotFinger: {
|
|
{Name: "Wooden Ring", FormID: "gear.form.finger.ring", BasePrimary: 2, StatType: "mixed"},
|
|
{Name: "Silver Ring", FormID: "gear.form.finger.ring", BasePrimary: 4, StatType: "attack"},
|
|
{Name: "Crystal Ring", FormID: "gear.form.finger.ring", BasePrimary: 3, StatType: "defense"},
|
|
},
|
|
SlotWrist: {
|
|
{Name: "Leather Bracers", FormID: "gear.form.wrist.bracers", BasePrimary: 2, StatType: "defense"},
|
|
{Name: "Iron Wristguards", FormID: "gear.form.wrist.wristguards", BasePrimary: 4, StatType: "defense"},
|
|
{Name: "Dragonhide Wristguards", FormID: "gear.form.wrist.wristguards", BasePrimary: 3, StatType: "mixed"},
|
|
},
|
|
}
|