last_light/game/model/rpg_generate_items.go

296 lines
7.4 KiB
Go
Raw Normal View History

2024-06-06 23:17:22 +03:00
package model
2024-05-21 23:08:51 +03:00
import (
"math/rand"
"mvvasilev/last_light/engine"
2024-05-30 23:39:54 +03:00
"slices"
"unicode"
"unicode/utf8"
2024-05-21 23:08:51 +03:00
"github.com/gdamore/tcell/v2"
"github.com/google/uuid"
)
2024-05-30 23:39:54 +03:00
const MaxNumberOfModifiers = 6
2024-06-06 23:17:22 +03:00
type ItemSupplier func() Item_V2
2024-05-21 23:08:51 +03:00
type LootTable struct {
table []ItemSupplier
}
func CreateLootTable() *LootTable {
return &LootTable{
table: make([]ItemSupplier, 0),
}
}
func (igt *LootTable) Add(weight int, createItemFunction ItemSupplier) {
for range weight {
igt.table = append(igt.table, createItemFunction)
}
}
2024-06-06 23:17:22 +03:00
func (igt *LootTable) Generate() Item_V2 {
2024-05-21 23:08:51 +03:00
return igt.table[rand.Intn(len(igt.table))]()
}
type ItemRarity int
const (
ItemRarity_Common ItemRarity = 0
ItemRarity_Uncommon ItemRarity = 1
ItemRarity_Rare ItemRarity = 2
ItemRarity_Epic ItemRarity = 3
ItemRarity_Legendary ItemRarity = 4
)
func pointPerRarity(rarity ItemRarity) int {
switch rarity {
case ItemRarity_Common:
return 0
case ItemRarity_Uncommon:
return 3
case ItemRarity_Rare:
return 5
case ItemRarity_Epic:
return 8
case ItemRarity_Legendary:
return 13
default:
return 0
}
}
2024-05-30 23:39:54 +03:00
func generateUniqueItemName() string {
starts := []string{
"du", "nol", "ma", "re",
"ka", "gro", "hru", "lo",
"ara", "ke", "ko", "uro",
"ne", "pe", "pa", "pho",
}
middles := []string{
"kora", "duru", "kolku", "dila",
"luio", "ghro", "kelma", "riga",
"fela", "fiya", "numa", "ruta",
}
end := []string{
"dum", "dor", "dar", "thar",
"thor", "thum", "hor", "hum",
"her", "kom", "kur", "kyr",
"mor", "mar", "man", "kum",
"tum",
}
name := starts[rand.Intn(len(starts))] + middles[rand.Intn(len(middles))] + end[rand.Intn(len(end))]
r, size := utf8.DecodeRuneInString(name)
return string(unicode.ToUpper(r)) + name[size:]
}
func randomAdjective() string {
adjectives := []string{
"shiny", "gruesome", "sharp", "tattered",
"mediocre", "unusual", "bright", "rusty",
"dreadful", "exceptional", "old", "bent",
"ancient", "crude", "dented", "cool",
}
adj := adjectives[rand.Intn(len(adjectives))]
r, size := utf8.DecodeRuneInString(adj)
return string(unicode.ToUpper(r)) + adj[size:]
}
func randomSuffix() string {
suffixes := []string{
"of the Monkey", "of the Tiger", "of the Elephant", "of the Slug",
"of Elven Make",
}
return suffixes[rand.Intn(len(suffixes))]
}
2024-06-06 23:17:22 +03:00
func generateItemName(existingItemName string, rarity ItemRarity) (string, tcell.Style) {
2024-05-21 23:08:51 +03:00
switch rarity {
case ItemRarity_Common:
2024-06-06 23:17:22 +03:00
return existingItemName, tcell.StyleDefault
2024-05-21 23:08:51 +03:00
case ItemRarity_Uncommon:
2024-06-06 23:17:22 +03:00
return randomAdjective() + " " + existingItemName, tcell.StyleDefault.Foreground(tcell.ColorLime)
2024-05-21 23:08:51 +03:00
case ItemRarity_Rare:
2024-06-06 23:17:22 +03:00
return existingItemName + " " + randomSuffix(), tcell.StyleDefault.Foreground(tcell.ColorBlue)
2024-05-21 23:08:51 +03:00
case ItemRarity_Epic:
2024-06-06 23:17:22 +03:00
return randomAdjective() + " " + existingItemName + " " + randomSuffix(), tcell.StyleDefault.Foreground(tcell.ColorPurple)
2024-05-21 23:08:51 +03:00
case ItemRarity_Legendary:
2024-06-06 23:17:22 +03:00
return generateUniqueItemName() + ", Legendary " + existingItemName, tcell.StyleDefault.Foreground(tcell.ColorOrange).Attributes(tcell.AttrBold)
2024-05-21 23:08:51 +03:00
default:
2024-06-06 23:17:22 +03:00
return existingItemName, tcell.StyleDefault
2024-05-21 23:08:51 +03:00
}
}
2024-06-06 23:17:22 +03:00
func randomStat(metaItemTypes []ItemMetaType) Stat {
stats := make(map[ItemMetaType][]Stat, 0)
2024-05-30 23:39:54 +03:00
stats[MetaItemType_Weapon] = []Stat{
2024-05-21 23:08:51 +03:00
Stat_Attributes_Strength,
Stat_Attributes_Dexterity,
Stat_Attributes_Intelligence,
Stat_Attributes_Constitution,
Stat_TotalPrecisionBonus,
2024-05-30 23:39:54 +03:00
}
stats[MetaItemType_Physical_Weapon] = []Stat{
Stat_PhysicalPrecisionBonus,
2024-05-21 23:08:51 +03:00
Stat_DamageBonus_Physical_Slashing,
Stat_DamageBonus_Physical_Piercing,
Stat_DamageBonus_Physical_Bludgeoning,
Stat_DamageBonus_Magic_Fire,
Stat_DamageBonus_Magic_Cold,
Stat_DamageBonus_Magic_Necrotic,
Stat_DamageBonus_Magic_Thunder,
Stat_DamageBonus_Magic_Acid,
Stat_DamageBonus_Magic_Poison,
2024-05-30 23:39:54 +03:00
}
stats[MetaItemType_Magic_Weapon] = []Stat{
Stat_MagicPrecisionBonus,
Stat_DamageBonus_Magic_Fire,
Stat_DamageBonus_Magic_Cold,
Stat_DamageBonus_Magic_Necrotic,
Stat_DamageBonus_Magic_Thunder,
Stat_DamageBonus_Magic_Acid,
Stat_DamageBonus_Magic_Poison,
}
stats[MetaItemType_Armour] = []Stat{
Stat_EvasionBonus,
Stat_DamageBonus_Physical_Unarmed,
2024-05-21 23:08:51 +03:00
Stat_MaxHealthBonus,
}
2024-05-30 23:39:54 +03:00
stats[MetaItemType_Magic_Armour] = []Stat{
Stat_MagicPrecisionBonus,
Stat_DamageBonus_Magic_Fire,
Stat_DamageBonus_Magic_Cold,
Stat_DamageBonus_Magic_Necrotic,
Stat_DamageBonus_Magic_Thunder,
Stat_DamageBonus_Magic_Acid,
Stat_DamageBonus_Magic_Poison,
2024-06-06 23:17:22 +03:00
Stat_ResistanceBonus_Magic_Acid,
Stat_ResistanceBonus_Magic_Cold,
Stat_ResistanceBonus_Magic_Fire,
Stat_ResistanceBonus_Magic_Necrotic,
Stat_ResistanceBonus_Magic_Poison,
Stat_ResistanceBonus_Magic_Thunder,
2024-05-30 23:39:54 +03:00
}
stats[MetaItemType_Physical_Armour] = []Stat{
Stat_PhysicalPrecisionBonus,
Stat_DamageBonus_Physical_Slashing,
Stat_DamageBonus_Physical_Piercing,
Stat_DamageBonus_Physical_Bludgeoning,
2024-06-06 23:17:22 +03:00
Stat_ResistanceBonus_Physical_Bludgeoning,
Stat_ResistanceBonus_Physical_Piercing,
Stat_ResistanceBonus_Physical_Slashing,
Stat_ResistanceBonus_Physical_Unarmed,
2024-05-30 23:39:54 +03:00
}
possibleStats := make([]Stat, 0, 10)
for _, mt := range metaItemTypes {
possibleStats = append(possibleStats, stats[mt]...)
}
return slices.Compact(possibleStats)[rand.Intn(len(stats))]
2024-05-21 23:08:51 +03:00
}
2024-06-06 23:17:22 +03:00
func generateItemStatModifiers(itemMetaTypes []ItemMetaType, rarity ItemRarity) []StatModifier {
2024-05-21 23:08:51 +03:00
points := pointPerRarity(rarity)
2024-05-30 23:39:54 +03:00
modifiers := make(map[Stat]*StatModifier, 0)
2024-05-21 23:08:51 +03:00
for {
2024-05-30 23:39:54 +03:00
// If no points remain, or if the number of modifiers on the item reaches the maximum
if points <= 0 || len(modifiers) == MaxNumberOfModifiers {
2024-05-21 23:08:51 +03:00
break
}
2024-05-30 23:39:54 +03:00
// Random chance to increase or decrease a stat
modAmount := engine.RandInt(-points, points)
2024-05-21 23:08:51 +03:00
if modAmount == 0 {
continue
}
2024-06-06 23:17:22 +03:00
stat := randomStat(itemMetaTypes)
2024-05-30 23:39:54 +03:00
existingForStat := modifiers[stat]
// If this stat modifier already exists on the item, add the new modification amount to the old
if existingForStat != nil {
existingForStat.Bonus += modAmount
// If the added amount is 0, remove the modifier
if existingForStat.Bonus == 0 {
delete(modifiers, stat)
} else {
modifiers[stat] = existingForStat
}
} else {
// Otherwise, append a new stat modifier
modifiers[stat] = &StatModifier{
Id: StatModifierId(uuid.New().String()),
Stat: stat,
Bonus: modAmount,
}
}
// Decrease amount of points left by absolute value
points -= engine.AbsInt(modAmount)
}
vals := make([]StatModifier, 0, len(modifiers))
2024-05-21 23:08:51 +03:00
2024-05-30 23:39:54 +03:00
for _, v := range modifiers {
vals = append(vals, *v)
2024-05-21 23:08:51 +03:00
}
2024-05-30 23:39:54 +03:00
return vals
2024-05-21 23:08:51 +03:00
}
// Each rarity gets an amount of generation points, the higher the rarity, the more points
// Each stat modifier consumes points. The higher the stat bonus, the more points it consumes.
2024-06-06 23:17:22 +03:00
func GenerateItemOfTypeAndRarity(prototype Item_V2, rarity ItemRarity) Item_V2 {
if prototype.Named() == nil {
return prototype
}
2024-05-21 23:08:51 +03:00
2024-06-06 23:17:22 +03:00
if prototype.MetaTypes() == nil {
return prototype
}
existingName := prototype.Named().Name
metaTypes := prototype.MetaTypes().Types
// points := pointPerRarity(rarity)
name, style := generateItemName(existingName, rarity)
statModifiers := generateItemStatModifiers(metaTypes, rarity)
return createBaseItem(
prototype.Type(),
prototype.TileIcon(),
prototype.Icon(),
prototype.Style(),
item_WithName(name, style),
item_WithDescription(prototype.Described().Description, prototype.Described().Style),
item_WithDamaging(prototype.Damaging().DamageRoll),
item_WithEquippable(prototype.Equippable().Slot),
item_WithStatModifiers(statModifiers),
item_WithMetaTypes(metaTypes),
2024-05-21 23:08:51 +03:00
)
}