big item rework
This commit is contained in:
parent
c226f6200f
commit
bbd515bc57
BIN
data/dk.db
BIN
data/dk.db
Binary file not shown.
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module dk
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.sharkk.net/Sharkk/Sashimi v1.1.1
|
git.sharkk.net/Sharkk/Sashimi v1.1.3
|
||||||
git.sharkk.net/Sharkk/Sushi v1.2.0
|
git.sharkk.net/Sharkk/Sushi v1.2.0
|
||||||
github.com/valyala/fasthttp v1.65.0
|
github.com/valyala/fasthttp v1.65.0
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -1,5 +1,5 @@
|
|||||||
git.sharkk.net/Sharkk/Sashimi v1.1.1 h1:ST7YQjnQq3ydAKy/Kerx89cx+b/mXDOq7W/ll1aX1jA=
|
git.sharkk.net/Sharkk/Sashimi v1.1.3 h1:fY63Zn//A1EffFkoKjCQseRmLFNRibNDZYPUur5SF1s=
|
||||||
git.sharkk.net/Sharkk/Sashimi v1.1.1/go.mod h1:wTMnO6jo34LIjpDJ0qToq14RbwP6Uf4HtdWDmqxrdAM=
|
git.sharkk.net/Sharkk/Sashimi v1.1.3/go.mod h1:wTMnO6jo34LIjpDJ0qToq14RbwP6Uf4HtdWDmqxrdAM=
|
||||||
git.sharkk.net/Sharkk/Sushi v1.2.0 h1:RwOCZmgaOqtkmuK2Z7/esdLbhSXJZphsOsWEHni4Sss=
|
git.sharkk.net/Sharkk/Sushi v1.2.0 h1:RwOCZmgaOqtkmuK2Z7/esdLbhSXJZphsOsWEHni4Sss=
|
||||||
git.sharkk.net/Sharkk/Sushi v1.2.0/go.mod h1:S84ACGkuZ+BKzBO4lb5WQnm5aw9+l7VSO2T1bjzxL3o=
|
git.sharkk.net/Sharkk/Sushi v1.2.0/go.mod h1:S84ACGkuZ+BKzBO4lb5WQnm5aw9+l7VSO2T1bjzxL3o=
|
||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
|
@ -51,7 +51,7 @@ func Move(user *users.User, dir Direction) (string, int, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !control.IsWithinWorldBounds(newX, newY) {
|
if !control.IsWithinWorldBounds(newX, newY) {
|
||||||
return user.Currently, user.X, user.Y, fmt.Errorf("You've hit the edge of the world.")
|
return user.Currently, user.X, user.Y, fmt.Errorf("you've hit the edge of the world")
|
||||||
}
|
}
|
||||||
|
|
||||||
if town, _ := towns.ByCoords(newX, newY); town != nil {
|
if town, _ := towns.ByCoords(newX, newY); town != nil {
|
||||||
|
@ -1,57 +1,359 @@
|
|||||||
package actions
|
package actions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"dk/internal/models/items"
|
"dk/internal/models/items"
|
||||||
"dk/internal/models/users"
|
"dk/internal/models/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserEquipItem calculates equipment updates for a user equipping an item.
|
// UserEquipItem calculates equipment updates for a user equipping an item.
|
||||||
// Returns map of database field updates without modifying the user struct.
|
// Returns map of database field updates without modifying the user struct.
|
||||||
func UserEquipItem(user *users.User, item *items.Item) map[string]any {
|
func UserEquipItem(user *users.User, item *items.Item) (map[string]any, error) {
|
||||||
|
if !item.IsEquippable() && !item.IsSlottable() {
|
||||||
|
return nil, fmt.Errorf("item %s is not equippable", item.Name)
|
||||||
|
}
|
||||||
|
|
||||||
updates := make(map[string]any)
|
updates := make(map[string]any)
|
||||||
|
|
||||||
// Calculate stat changes
|
// Calculate stat changes by removing old item and adding new item
|
||||||
newAttack := user.Attack
|
newStats := calculateNewStats(user, item)
|
||||||
newDefense := user.Defense
|
|
||||||
|
|
||||||
// Remove old item stats if slot occupied
|
// Update equipment slots and stats based on item type
|
||||||
switch item.Type {
|
switch item.Type {
|
||||||
case items.TypeWeapon:
|
case items.TypeWeapon:
|
||||||
if user.WeaponID != 0 {
|
|
||||||
if oldItem, err := items.Find(user.WeaponID); err == nil {
|
|
||||||
newAttack -= oldItem.Att
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add new item
|
|
||||||
newAttack += item.Att
|
|
||||||
updates["weapon_id"] = item.ID
|
updates["weapon_id"] = item.ID
|
||||||
updates["weapon_name"] = item.Name
|
updates["weapon_name"] = item.Name
|
||||||
|
|
||||||
case items.TypeArmor:
|
case items.TypeArmor:
|
||||||
if user.ArmorID != 0 {
|
|
||||||
if oldItem, err := items.Find(user.ArmorID); err == nil {
|
|
||||||
newDefense -= oldItem.Att
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add new item
|
|
||||||
newDefense += item.Att
|
|
||||||
updates["armor_id"] = item.ID
|
updates["armor_id"] = item.ID
|
||||||
updates["armor_name"] = item.Name
|
updates["armor_name"] = item.Name
|
||||||
|
|
||||||
case items.TypeShield:
|
case items.TypeShield:
|
||||||
if user.ShieldID != 0 {
|
|
||||||
if oldItem, err := items.Find(user.ShieldID); err == nil {
|
|
||||||
newDefense -= oldItem.Att
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add new item
|
|
||||||
newDefense += item.Att
|
|
||||||
updates["shield_id"] = item.ID
|
updates["shield_id"] = item.ID
|
||||||
updates["shield_name"] = item.Name
|
updates["shield_name"] = item.Name
|
||||||
|
|
||||||
|
case items.TypeAccessory:
|
||||||
|
// Find first available slot or replace slot 1
|
||||||
|
if user.Slot1ID == 0 {
|
||||||
|
updates["slot_1_id"] = item.ID
|
||||||
|
updates["slot_1_name"] = item.Name
|
||||||
|
} else if user.Slot2ID == 0 {
|
||||||
|
updates["slot_2_id"] = item.ID
|
||||||
|
updates["slot_2_name"] = item.Name
|
||||||
|
} else if user.Slot3ID == 0 {
|
||||||
|
updates["slot_3_id"] = item.ID
|
||||||
|
updates["slot_3_name"] = item.Name
|
||||||
|
} else {
|
||||||
|
// All slots full, replace slot 1
|
||||||
|
updates["slot_1_id"] = item.ID
|
||||||
|
updates["slot_1_name"] = item.Name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updates["attack"] = newAttack
|
// Apply stat changes
|
||||||
updates["defense"] = newDefense
|
updates["attack"] = newStats.Attack
|
||||||
|
updates["defense"] = newStats.Defense
|
||||||
|
updates["strength"] = newStats.Strength
|
||||||
|
updates["dexterity"] = newStats.Dexterity
|
||||||
|
updates["max_hp"] = newStats.MaxHP
|
||||||
|
updates["max_mp"] = newStats.MaxMP
|
||||||
|
updates["exp_bonus"] = newStats.ExpBonus
|
||||||
|
updates["gold_bonus"] = newStats.GoldBonus
|
||||||
|
|
||||||
return updates
|
return updates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserEquipAccessoryToSlot equips an accessory to a specific slot (1-3)
|
||||||
|
func UserEquipAccessoryToSlot(user *users.User, item *items.Item, slot int) (map[string]any, error) {
|
||||||
|
if !item.IsSlottable() {
|
||||||
|
return nil, fmt.Errorf("item %s is not slottable", item.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if slot < 1 || slot > 3 {
|
||||||
|
return nil, fmt.Errorf("invalid slot number: %d", slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := make(map[string]any)
|
||||||
|
newStats := calculateNewStatsForSlot(user, item, slot)
|
||||||
|
|
||||||
|
// Update the specific slot
|
||||||
|
switch slot {
|
||||||
|
case 1:
|
||||||
|
updates["slot_1_id"] = item.ID
|
||||||
|
updates["slot_1_name"] = item.Name
|
||||||
|
case 2:
|
||||||
|
updates["slot_2_id"] = item.ID
|
||||||
|
updates["slot_2_name"] = item.Name
|
||||||
|
case 3:
|
||||||
|
updates["slot_3_id"] = item.ID
|
||||||
|
updates["slot_3_name"] = item.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply stat changes
|
||||||
|
updates["attack"] = newStats.Attack
|
||||||
|
updates["defense"] = newStats.Defense
|
||||||
|
updates["strength"] = newStats.Strength
|
||||||
|
updates["dexterity"] = newStats.Dexterity
|
||||||
|
updates["max_hp"] = newStats.MaxHP
|
||||||
|
updates["max_mp"] = newStats.MaxMP
|
||||||
|
updates["exp_bonus"] = newStats.ExpBonus
|
||||||
|
updates["gold_bonus"] = newStats.GoldBonus
|
||||||
|
|
||||||
|
return updates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserUnequipItem removes an equipped item and recalculates stats
|
||||||
|
func UserUnequipItem(user *users.User, itemType int) (map[string]any, error) {
|
||||||
|
updates := make(map[string]any)
|
||||||
|
|
||||||
|
switch itemType {
|
||||||
|
case items.TypeWeapon:
|
||||||
|
if user.WeaponID == 0 {
|
||||||
|
return nil, fmt.Errorf("no weapon equipped")
|
||||||
|
}
|
||||||
|
updates["weapon_id"] = 0
|
||||||
|
updates["weapon_name"] = ""
|
||||||
|
|
||||||
|
case items.TypeArmor:
|
||||||
|
if user.ArmorID == 0 {
|
||||||
|
return nil, fmt.Errorf("no armor equipped")
|
||||||
|
}
|
||||||
|
updates["armor_id"] = 0
|
||||||
|
updates["armor_name"] = ""
|
||||||
|
|
||||||
|
case items.TypeShield:
|
||||||
|
if user.ShieldID == 0 {
|
||||||
|
return nil, fmt.Errorf("no shield equipped")
|
||||||
|
}
|
||||||
|
updates["shield_id"] = 0
|
||||||
|
updates["shield_name"] = ""
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid item type for unequip: %d", itemType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate all stats after unequipping
|
||||||
|
newStats := recalculateAllStats(user, itemType, 0)
|
||||||
|
updates["attack"] = newStats.Attack
|
||||||
|
updates["defense"] = newStats.Defense
|
||||||
|
updates["strength"] = newStats.Strength
|
||||||
|
updates["dexterity"] = newStats.Dexterity
|
||||||
|
updates["max_hp"] = newStats.MaxHP
|
||||||
|
updates["max_mp"] = newStats.MaxMP
|
||||||
|
updates["exp_bonus"] = newStats.ExpBonus
|
||||||
|
updates["gold_bonus"] = newStats.GoldBonus
|
||||||
|
|
||||||
|
return updates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserUnequipAccessory removes an accessory from a specific slot
|
||||||
|
func UserUnequipAccessory(user *users.User, slot int) (map[string]any, error) {
|
||||||
|
if slot < 1 || slot > 3 {
|
||||||
|
return nil, fmt.Errorf("invalid slot number: %d", slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := make(map[string]any)
|
||||||
|
|
||||||
|
// Check if slot has an item
|
||||||
|
switch slot {
|
||||||
|
case 1:
|
||||||
|
if user.Slot1ID == 0 {
|
||||||
|
return nil, fmt.Errorf("slot 1 is empty")
|
||||||
|
}
|
||||||
|
updates["slot_1_id"] = 0
|
||||||
|
updates["slot_1_name"] = ""
|
||||||
|
case 2:
|
||||||
|
if user.Slot2ID == 0 {
|
||||||
|
return nil, fmt.Errorf("slot 2 is empty")
|
||||||
|
}
|
||||||
|
updates["slot_2_id"] = 0
|
||||||
|
updates["slot_2_name"] = ""
|
||||||
|
case 3:
|
||||||
|
if user.Slot3ID == 0 {
|
||||||
|
return nil, fmt.Errorf("slot 3 is empty")
|
||||||
|
}
|
||||||
|
updates["slot_3_id"] = 0
|
||||||
|
updates["slot_3_name"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate stats after removing accessory
|
||||||
|
newStats := recalculateAllStats(user, items.TypeAccessory, slot)
|
||||||
|
updates["attack"] = newStats.Attack
|
||||||
|
updates["defense"] = newStats.Defense
|
||||||
|
updates["strength"] = newStats.Strength
|
||||||
|
updates["dexterity"] = newStats.Dexterity
|
||||||
|
updates["max_hp"] = newStats.MaxHP
|
||||||
|
updates["max_mp"] = newStats.MaxMP
|
||||||
|
updates["exp_bonus"] = newStats.ExpBonus
|
||||||
|
updates["gold_bonus"] = newStats.GoldBonus
|
||||||
|
|
||||||
|
return updates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats represents calculated user stats
|
||||||
|
type Stats struct {
|
||||||
|
Attack int
|
||||||
|
Defense int
|
||||||
|
Strength int
|
||||||
|
Dexterity int
|
||||||
|
MaxHP int
|
||||||
|
MaxMP int
|
||||||
|
ExpBonus int
|
||||||
|
GoldBonus int
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateNewStats calculates new stats when equipping an item
|
||||||
|
func calculateNewStats(user *users.User, newItem *items.Item) Stats {
|
||||||
|
stats := Stats{
|
||||||
|
Attack: user.Attack,
|
||||||
|
Defense: user.Defense,
|
||||||
|
Strength: user.Strength,
|
||||||
|
Dexterity: user.Dexterity,
|
||||||
|
MaxHP: user.MaxHP,
|
||||||
|
MaxMP: user.MaxMP,
|
||||||
|
ExpBonus: user.ExpBonus,
|
||||||
|
GoldBonus: user.GoldBonus,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old item stats if slot occupied
|
||||||
|
switch newItem.Type {
|
||||||
|
case items.TypeWeapon:
|
||||||
|
if user.WeaponID != 0 {
|
||||||
|
if oldItem, err := items.Find(user.WeaponID); err == nil {
|
||||||
|
removeItemStats(&stats, oldItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case items.TypeArmor:
|
||||||
|
if user.ArmorID != 0 {
|
||||||
|
if oldItem, err := items.Find(user.ArmorID); err == nil {
|
||||||
|
removeItemStats(&stats, oldItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case items.TypeShield:
|
||||||
|
if user.ShieldID != 0 {
|
||||||
|
if oldItem, err := items.Find(user.ShieldID); err == nil {
|
||||||
|
removeItemStats(&stats, oldItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new item stats
|
||||||
|
addItemStats(&stats, newItem)
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateNewStatsForSlot calculates stats when equipping to a specific accessory slot
|
||||||
|
func calculateNewStatsForSlot(user *users.User, newItem *items.Item, slot int) Stats {
|
||||||
|
stats := Stats{
|
||||||
|
Attack: user.Attack,
|
||||||
|
Defense: user.Defense,
|
||||||
|
Strength: user.Strength,
|
||||||
|
Dexterity: user.Dexterity,
|
||||||
|
MaxHP: user.MaxHP,
|
||||||
|
MaxMP: user.MaxMP,
|
||||||
|
ExpBonus: user.ExpBonus,
|
||||||
|
GoldBonus: user.GoldBonus,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old accessory stats from the specified slot
|
||||||
|
var oldItemID int
|
||||||
|
switch slot {
|
||||||
|
case 1:
|
||||||
|
oldItemID = user.Slot1ID
|
||||||
|
case 2:
|
||||||
|
oldItemID = user.Slot2ID
|
||||||
|
case 3:
|
||||||
|
oldItemID = user.Slot3ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldItemID != 0 {
|
||||||
|
if oldItem, err := items.Find(oldItemID); err == nil {
|
||||||
|
removeItemStats(&stats, oldItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new item stats
|
||||||
|
addItemStats(&stats, newItem)
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// recalculateAllStats recalculates all stats from scratch, excluding specified item
|
||||||
|
func recalculateAllStats(user *users.User, excludeType, excludeSlot int) Stats {
|
||||||
|
// Start with base stats (these would come from class/level progression)
|
||||||
|
// For now, using current stats minus all equipment bonuses
|
||||||
|
stats := Stats{
|
||||||
|
Attack: 0, // Base attack from strength/level
|
||||||
|
Defense: 0, // Base defense from dexterity/level
|
||||||
|
Strength: user.Strength,
|
||||||
|
Dexterity: user.Dexterity,
|
||||||
|
MaxHP: user.MaxHP,
|
||||||
|
MaxMP: user.MaxMP,
|
||||||
|
ExpBonus: 0, // Base exp bonus
|
||||||
|
GoldBonus: 0, // Base gold bonus
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add weapon stats (unless being removed)
|
||||||
|
if user.WeaponID != 0 && excludeType != items.TypeWeapon {
|
||||||
|
if item, err := items.Find(user.WeaponID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add armor stats (unless being removed)
|
||||||
|
if user.ArmorID != 0 && excludeType != items.TypeArmor {
|
||||||
|
if item, err := items.Find(user.ArmorID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add shield stats (unless being removed)
|
||||||
|
if user.ShieldID != 0 && excludeType != items.TypeShield {
|
||||||
|
if item, err := items.Find(user.ShieldID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add accessory stats (unless specific slot being removed)
|
||||||
|
if user.Slot1ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 1) {
|
||||||
|
if item, err := items.Find(user.Slot1ID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.Slot2ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 2) {
|
||||||
|
if item, err := items.Find(user.Slot2ID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.Slot3ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 3) {
|
||||||
|
if item, err := items.Find(user.Slot3ID); err == nil {
|
||||||
|
addItemStats(&stats, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// addItemStats adds an item's stats to the current stats
|
||||||
|
func addItemStats(stats *Stats, item *items.Item) {
|
||||||
|
stats.Attack += item.Attack
|
||||||
|
stats.Defense += item.Defense
|
||||||
|
stats.Strength += item.Strength
|
||||||
|
stats.Dexterity += item.Dexterity
|
||||||
|
stats.MaxHP += item.MaxHP
|
||||||
|
stats.MaxMP += item.MaxMP
|
||||||
|
stats.ExpBonus += item.ExpBonus
|
||||||
|
stats.GoldBonus += item.GoldBonus
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeItemStats removes an item's stats from the current stats
|
||||||
|
func removeItemStats(stats *Stats, item *items.Item) {
|
||||||
|
stats.Attack -= item.Attack
|
||||||
|
stats.Defense -= item.Defense
|
||||||
|
stats.Strength -= item.Strength
|
||||||
|
stats.Dexterity -= item.Dexterity
|
||||||
|
stats.MaxHP -= item.MaxHP
|
||||||
|
stats.MaxMP -= item.MaxMP
|
||||||
|
stats.ExpBonus -= item.ExpBonus
|
||||||
|
stats.GoldBonus -= item.GoldBonus
|
||||||
}
|
}
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package drops
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"dk/internal/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Drop represents a drop item in the game
|
|
||||||
type Drop struct {
|
|
||||||
ID int
|
|
||||||
Name string
|
|
||||||
Level int
|
|
||||||
Type int
|
|
||||||
Att string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropType constants for drop types
|
|
||||||
const (
|
|
||||||
TypeConsumable = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// New creates a new Drop with sensible defaults
|
|
||||||
func New() *Drop {
|
|
||||||
return &Drop{
|
|
||||||
Name: "",
|
|
||||||
Level: 1,
|
|
||||||
Type: TypeConsumable,
|
|
||||||
Att: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks if drop has valid values
|
|
||||||
func (d *Drop) Validate() error {
|
|
||||||
if d.Name == "" {
|
|
||||||
return fmt.Errorf("drop name cannot be empty")
|
|
||||||
}
|
|
||||||
if d.Level < 1 {
|
|
||||||
return fmt.Errorf("drop Level must be at least 1")
|
|
||||||
}
|
|
||||||
if d.Type < TypeConsumable {
|
|
||||||
return fmt.Errorf("invalid drop type: %d", d.Type)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRUD operations
|
|
||||||
func (d *Drop) Delete() error {
|
|
||||||
return database.Exec("DELETE FROM drops WHERE id = %d", d.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drop) Insert() error {
|
|
||||||
id, err := database.Insert("drops", d, "ID")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.ID = int(id)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query functions
|
|
||||||
func Find(id int) (*Drop, error) {
|
|
||||||
var drop Drop
|
|
||||||
err := database.Get(&drop, "SELECT * FROM drops WHERE id = %d", id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("drop with ID %d not found", id)
|
|
||||||
}
|
|
||||||
return &drop, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func All() ([]*Drop, error) {
|
|
||||||
var drops []*Drop
|
|
||||||
err := database.Select(&drops, "SELECT * FROM drops ORDER BY id ASC")
|
|
||||||
return drops, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ByLevel(minLevel int) ([]*Drop, error) {
|
|
||||||
var drops []*Drop
|
|
||||||
err := database.Select(&drops, "SELECT * FROM drops WHERE level <= %d ORDER BY id ASC", minLevel)
|
|
||||||
return drops, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ByType(dropType int) ([]*Drop, error) {
|
|
||||||
var drops []*Drop
|
|
||||||
err := database.Select(&drops, "SELECT * FROM drops WHERE type = %d ORDER BY id ASC", dropType)
|
|
||||||
return drops, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
func (d *Drop) IsConsumable() bool {
|
|
||||||
return d.Type == TypeConsumable
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drop) TypeName() string {
|
|
||||||
switch d.Type {
|
|
||||||
case TypeConsumable:
|
|
||||||
return "Consumable"
|
|
||||||
default:
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,29 +8,46 @@ import (
|
|||||||
|
|
||||||
// Item represents an item in the game
|
// Item represents an item in the game
|
||||||
type Item struct {
|
type Item struct {
|
||||||
ID int
|
ID int `db:"id"`
|
||||||
Type int
|
Type int `db:"type"`
|
||||||
Name string
|
Name string `db:"name"`
|
||||||
Value int
|
Lore string `db:"lore"`
|
||||||
Att int
|
Value int `db:"value"`
|
||||||
Special string
|
Attack int `db:"attack"`
|
||||||
|
Defense int `db:"defense"`
|
||||||
|
Strength int `db:"strength"`
|
||||||
|
Dexterity int `db:"dexterity"`
|
||||||
|
MaxHP int `db:"max_hp"`
|
||||||
|
MaxMP int `db:"max_mp"`
|
||||||
|
ExpBonus int `db:"exp_bonus"`
|
||||||
|
GoldBonus int `db:"gold_bonus"`
|
||||||
|
Special string `db:"special"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemType constants for item types
|
// ItemType constants for item types
|
||||||
const (
|
const (
|
||||||
TypeWeapon = 1
|
TypeWeapon = 1
|
||||||
TypeArmor = 2
|
TypeArmor = 2
|
||||||
TypeShield = 3
|
TypeShield = 3
|
||||||
|
TypeAccessory = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new Item with sensible defaults
|
// New creates a new Item with sensible defaults
|
||||||
func New() *Item {
|
func New() *Item {
|
||||||
return &Item{
|
return &Item{
|
||||||
Type: TypeWeapon,
|
Type: TypeWeapon,
|
||||||
Name: "",
|
Name: "",
|
||||||
Value: 0,
|
Lore: "",
|
||||||
Att: 0,
|
Value: 0,
|
||||||
Special: "",
|
Attack: 0,
|
||||||
|
Defense: 0,
|
||||||
|
Strength: 0,
|
||||||
|
Dexterity: 0,
|
||||||
|
MaxHP: 0,
|
||||||
|
MaxMP: 0,
|
||||||
|
ExpBonus: 0,
|
||||||
|
GoldBonus: 0,
|
||||||
|
Special: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,14 +56,11 @@ func (i *Item) Validate() error {
|
|||||||
if i.Name == "" {
|
if i.Name == "" {
|
||||||
return fmt.Errorf("item name cannot be empty")
|
return fmt.Errorf("item name cannot be empty")
|
||||||
}
|
}
|
||||||
if i.Type < TypeWeapon || i.Type > TypeShield {
|
if i.Type < TypeWeapon || i.Type > TypeAccessory {
|
||||||
return fmt.Errorf("invalid item type: %d", i.Type)
|
return fmt.Errorf("invalid item type: %d", i.Type)
|
||||||
}
|
}
|
||||||
if i.Value < 0 {
|
if i.Value < 0 {
|
||||||
return fmt.Errorf("item Value cannot be negative")
|
return fmt.Errorf("item value cannot be negative")
|
||||||
}
|
|
||||||
if i.Att < 0 {
|
|
||||||
return fmt.Errorf("item Att cannot be negative")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -57,7 +71,7 @@ func (i *Item) Delete() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) Insert() error {
|
func (i *Item) Insert() error {
|
||||||
id, err := database.Insert("items", i, "ID")
|
id, err := database.Insert("items", i, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -65,6 +79,25 @@ func (i *Item) Insert() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Item) Update() error {
|
||||||
|
fields := map[string]any{
|
||||||
|
"type": i.Type,
|
||||||
|
"name": i.Name,
|
||||||
|
"lore": i.Lore,
|
||||||
|
"value": i.Value,
|
||||||
|
"attack": i.Attack,
|
||||||
|
"defense": i.Defense,
|
||||||
|
"strength": i.Strength,
|
||||||
|
"dexterity": i.Dexterity,
|
||||||
|
"max_hp": i.MaxHP,
|
||||||
|
"max_mp": i.MaxMP,
|
||||||
|
"exp_bonus": i.ExpBonus,
|
||||||
|
"gold_bonus": i.GoldBonus,
|
||||||
|
"special": i.Special,
|
||||||
|
}
|
||||||
|
return database.Update("items", fields, "id", i.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Item, error) {
|
func Find(id int) (*Item, error) {
|
||||||
var item Item
|
var item Item
|
||||||
@ -77,13 +110,19 @@ func Find(id int) (*Item, error) {
|
|||||||
|
|
||||||
func All() ([]*Item, error) {
|
func All() ([]*Item, error) {
|
||||||
var items []*Item
|
var items []*Item
|
||||||
err := database.Select(&items, "SELECT * FROM items ORDER BY id ASC")
|
err := database.Select(&items, "SELECT * FROM items ORDER BY type ASC, value ASC, id ASC")
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByType(itemType int) ([]*Item, error) {
|
func ByType(itemType int) ([]*Item, error) {
|
||||||
var items []*Item
|
var items []*Item
|
||||||
err := database.Select(&items, "SELECT * FROM items WHERE type = %d ORDER BY id ASC", itemType)
|
err := database.Select(&items, "SELECT * FROM items WHERE type = %d ORDER BY value ASC, id ASC", itemType)
|
||||||
|
return items, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ByValueRange(minValue, maxValue int) ([]*Item, error) {
|
||||||
|
var items []*Item
|
||||||
|
err := database.Select(&items, "SELECT * FROM items WHERE value >= %d AND value <= %d ORDER BY value ASC, id ASC", minValue, maxValue)
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +139,10 @@ func (i *Item) IsShield() bool {
|
|||||||
return i.Type == TypeShield
|
return i.Type == TypeShield
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Item) IsAccessory() bool {
|
||||||
|
return i.Type == TypeAccessory
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Item) TypeName() string {
|
func (i *Item) TypeName() string {
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case TypeWeapon:
|
case TypeWeapon:
|
||||||
@ -108,6 +151,8 @@ func (i *Item) TypeName() string {
|
|||||||
return "Armor"
|
return "Armor"
|
||||||
case TypeShield:
|
case TypeShield:
|
||||||
return "Shield"
|
return "Shield"
|
||||||
|
case TypeAccessory:
|
||||||
|
return "Accessory"
|
||||||
default:
|
default:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
@ -118,5 +163,113 @@ func (i *Item) HasSpecial() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) IsEquippable() bool {
|
func (i *Item) IsEquippable() bool {
|
||||||
return i.Type == TypeWeapon || i.Type == TypeArmor || i.Type == TypeShield
|
return i.Type >= TypeWeapon && i.Type <= TypeShield
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) IsSlottable() bool {
|
||||||
|
return i.Type == TypeAccessory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasLore() bool {
|
||||||
|
return i.Lore != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat bonus methods
|
||||||
|
func (i *Item) HasAttackBonus() bool {
|
||||||
|
return i.Attack != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasDefenseBonus() bool {
|
||||||
|
return i.Defense != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasStrengthBonus() bool {
|
||||||
|
return i.Strength != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasDexterityBonus() bool {
|
||||||
|
return i.Dexterity != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasHPBonus() bool {
|
||||||
|
return i.MaxHP != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasMPBonus() bool {
|
||||||
|
return i.MaxMP != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasExpBonus() bool {
|
||||||
|
return i.ExpBonus != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) HasGoldBonus() bool {
|
||||||
|
return i.GoldBonus != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the item provides any stat bonuses
|
||||||
|
func (i *Item) HasStatBonuses() bool {
|
||||||
|
return i.Attack != 0 || i.Defense != 0 || i.Strength != 0 ||
|
||||||
|
i.Dexterity != 0 || i.MaxHP != 0 || i.MaxMP != 0 ||
|
||||||
|
i.ExpBonus != 0 || i.GoldBonus != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Item) GetStatBonuses() map[string]int {
|
||||||
|
bonuses := make(map[string]int)
|
||||||
|
|
||||||
|
if i.Attack != 0 {
|
||||||
|
bonuses["Attack"] = i.Attack
|
||||||
|
}
|
||||||
|
if i.Defense != 0 {
|
||||||
|
bonuses["Defense"] = i.Defense
|
||||||
|
}
|
||||||
|
if i.Strength != 0 {
|
||||||
|
bonuses["Strength"] = i.Strength
|
||||||
|
}
|
||||||
|
if i.Dexterity != 0 {
|
||||||
|
bonuses["Dexterity"] = i.Dexterity
|
||||||
|
}
|
||||||
|
if i.MaxHP != 0 {
|
||||||
|
bonuses["Max HP"] = i.MaxHP
|
||||||
|
}
|
||||||
|
if i.MaxMP != 0 {
|
||||||
|
bonuses["Max MP"] = i.MaxMP
|
||||||
|
}
|
||||||
|
if i.ExpBonus != 0 {
|
||||||
|
bonuses["Exp Bonus"] = i.ExpBonus
|
||||||
|
}
|
||||||
|
if i.GoldBonus != 0 {
|
||||||
|
bonuses["Gold Bonus"] = i.GoldBonus
|
||||||
|
}
|
||||||
|
|
||||||
|
return bonuses
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrimaryStatBonus returns the main stat bonus for the item type
|
||||||
|
func (i *Item) GetPrimaryStatBonus() int {
|
||||||
|
switch i.Type {
|
||||||
|
case TypeWeapon:
|
||||||
|
return i.Attack
|
||||||
|
case TypeArmor, TypeShield:
|
||||||
|
return i.Defense
|
||||||
|
case TypeAccessory:
|
||||||
|
// For accessories, return the highest stat bonus
|
||||||
|
max := 0
|
||||||
|
stats := []int{i.Attack, i.Defense, i.Strength, i.Dexterity, i.MaxHP, i.MaxMP}
|
||||||
|
for _, stat := range stats {
|
||||||
|
if abs(stat) > abs(max) {
|
||||||
|
max = stat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(x int) int {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,12 @@ func buyItem(ctx sushi.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get equipment updates from actions
|
// Get equipment updates from actions
|
||||||
equipUpdates := actions.UserEquipItem(user, item)
|
equipUpdates, err := actions.UserEquipItem(user, item)
|
||||||
|
if err != nil {
|
||||||
|
sess.SetFlash("error", "Cannot equip item: "+err.Error())
|
||||||
|
ctx.Redirect("/town/shop")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = database.Transaction(func() error {
|
err = database.Transaction(func() error {
|
||||||
// Start with gold deduction
|
// Start with gold deduction
|
||||||
|
@ -63,50 +63,6 @@ CREATE TABLE forum (
|
|||||||
`content` TEXT NOT NULL
|
`content` TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS items;
|
|
||||||
CREATE TABLE items (
|
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
`type` INTEGER NOT NULL DEFAULT 1,
|
|
||||||
`name` TEXT NOT NULL,
|
|
||||||
`value` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`att` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`special` TEXT NOT NULL DEFAULT ''
|
|
||||||
);
|
|
||||||
INSERT INTO items VALUES
|
|
||||||
(1, 1, 'Stick', 10, 2, ''),
|
|
||||||
(2, 1, 'Branch', 30, 4, ''),
|
|
||||||
(3, 1, 'Club', 40, 5, ''),
|
|
||||||
(4, 1, 'Dagger', 90, 8, ''),
|
|
||||||
(5, 1, 'Hatchet', 150, 12, ''),
|
|
||||||
(6, 1, 'Axe', 200, 16, ''),
|
|
||||||
(7, 1, 'Brand', 300, 25, ''),
|
|
||||||
(8, 1, 'Poleaxe', 500, 35, ''),
|
|
||||||
(9, 1, 'Broadsword', 800, 45, ''),
|
|
||||||
(10, 1, 'Battle Axe', 1200, 50, ''),
|
|
||||||
(11, 1, 'Claymore', 2000, 60, ''),
|
|
||||||
(12, 1, 'Dark Axe', 3000, 100, 'expbonus,-5'),
|
|
||||||
(13, 1, 'Dark Sword', 4500, 125, 'expbonus,-10'),
|
|
||||||
(14, 1, 'Bright Sword', 6000, 100, 'expbonus,10'),
|
|
||||||
(15, 1, 'Magic Sword', 10000, 150, 'maxmp,50'),
|
|
||||||
(16, 1, 'Destiny Blade', 50000, 250, 'strength,50'),
|
|
||||||
(17, 2, 'Skivvies', 25, 2, 'goldbonus,10'),
|
|
||||||
(18, 2, 'Clothes', 50, 5, ''),
|
|
||||||
(19, 2, 'Leather Armor', 75, 10, ''),
|
|
||||||
(20, 2, 'Hard Leather Armor', 150, 25, ''),
|
|
||||||
(21, 2, 'Chain Mail', 300, 30, ''),
|
|
||||||
(22, 2, 'Bronze Plate', 900, 50, ''),
|
|
||||||
(23, 2, 'Iron Plate', 2000, 100, ''),
|
|
||||||
(24, 2, 'Magic Armor', 4000, 125, 'maxmp,50'),
|
|
||||||
(25, 2, 'Dark Armor', 5000, 150, 'expbonus,-10'),
|
|
||||||
(26, 2, 'Bright Armor', 10000, 175, 'expbonus,10'),
|
|
||||||
(27, 2, 'Destiny Raiment', 50000, 200, 'dexterity,50'),
|
|
||||||
(28, 3, 'Reed Shield', 50, 2, ''),
|
|
||||||
(29, 3, 'Buckler', 100, 4, ''),
|
|
||||||
(30, 3, 'Small Shield', 500, 10, ''),
|
|
||||||
(31, 3, 'Large Shield', 2500, 30, ''),
|
|
||||||
(32, 3, 'Silver Shield', 10000, 60, ''),
|
|
||||||
(33, 3, 'Destiny Aegis', 25000, 100, 'maxhp,50');
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS classes;
|
DROP TABLE IF EXISTS classes;
|
||||||
CREATE TABLE classes (
|
CREATE TABLE classes (
|
||||||
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
|
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
88
sql/2_rework_items_system.sql
Normal file
88
sql/2_rework_items_system.sql
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
-- Migration 2: rework items system
|
||||||
|
-- Created: 2025-08-25 10:50:50
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS drops;
|
||||||
|
DROP TABLE IF EXISTS items;
|
||||||
|
|
||||||
|
CREATE TABLE items (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`type` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`name` TEXT NOT NULL,
|
||||||
|
`lore` TEXT DEFAULT '',
|
||||||
|
`value` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`attack` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`defense` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`strength` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`dexterity` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`max_hp` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`max_mp` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`exp_bonus` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`gold_bonus` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`special` TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
INSERT INTO items VALUES
|
||||||
|
(1, 1, 'Stick', 'Just a stick.', 10, 2, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(2, 1, 'Branch', 'Slightly larger stick.', 30, 4, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(3, 1, 'Club', 'Much larger stick.', 40, 5, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(4, 1, 'Dagger', 'Well-crafted knife.', 90, 8, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(5, 1, 'Hatchet', 'A smaller, sharpened axe.', 150, 12, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(6, 1, 'Axe', 'Full-sized, razor-sharp axe.', 200, 16, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(7, 1, 'Brand', 'An oddly-shaped, large knife.', 300, 25, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(8, 1, 'Poleaxe', 'A weighty axe on a long stick.', 500, 35, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(9, 1, 'Broadsword', 'Thick and heavy sword.', 800, 45, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(10, 1, 'Battle Axe', 'A large axe with spikes on the rear for catching armor.', 1200, 50, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(11, 1, 'Claymore', 'A very long sword made for cleaving through armor.', 2000, 60, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(12, 1, 'Dark Axe', 'Cursed axe with a dark aura - seems to be sharper than most, but strangely draining.', 3000, 100, 0, 0, 0, 0, 0, -5, 0, ''),
|
||||||
|
(13, 1, 'Dark Sword', 'Exceptionally sharp sword. The blade seems to shine with malice - you can feel it absorbing some of your will.', 4500, 125, 0, 0, 0, 0, 0, -10, 0, ''),
|
||||||
|
(14, 1, 'Bright Sword', 'The golden hilt and silver blade make this sword shine in the sun. Defeating enemies with it makes you feel fulfilled.', 6000, 100, 0, 0, 0, 0, 0, 10, 0, ''),
|
||||||
|
(15, 1, 'Magic Sword', 'One of Gilead''s mass-produced mage blades. Crafted with skill, enchanted by apprentices.', 10000, 150, 0, 0, 0, 0, 50, 0, 0, ''),
|
||||||
|
(16, 1, 'Destiny Blade', 'The sword that cleaves through demons like butter; a lost relic from humanity''s champion. Its immense strength is palpable.', 50000, 250, 0, 50, 0, 0, 0, 5, 5, ''),
|
||||||
|
(17, 2, 'Skivvies', 'Underwear! Plain and simple.', 25, 0, 2, 0, 0, 0, 0, 0, 10, ''),
|
||||||
|
(18, 2, 'Clothes', 'Standard working attire.', 50, 0, 5, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(19, 2, 'Leather Armor', 'Basic protection provided by supple hide.', 75, 0, 10, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(20, 2, 'Studded Armor', 'Leather armor enhanced with small metal buckles.', 150, 0, 25, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(21, 2, 'Chain Mail', 'Iron links, pieced together to form a dense shirt.', 300, 0, 30, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(22, 2, 'Bronze Plate', 'Pieces of bronze formed to your vital areas, secured with leather straps.', 900, 0, 50, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(23, 2, 'Iron Plate', 'Stronger and slightly lighter than bronze plates.', 2000, 0, 100, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(24, 2, 'Magic Armor', 'Steel armor crafted by the skilled artisans of Gilead, enchanted by apprentices.', 4000, 0, 125, 0, 0, 0, 50, 0, 0, ''),
|
||||||
|
(25, 2, 'Dark Armor', 'Armor that feels unnaturally heavy, although it also feels unnaturaly safe.', 5000, 0, 150, 0, 0, 0, 0, -10, 0, ''),
|
||||||
|
(26, 2, 'Bright Armor', 'These plates reflect the suns rays. You seem to understand your enemies attacks easier in it.', 10000, 0, 120, 0, 0, 0, 0, 10, 0, ''),
|
||||||
|
(27, 2, 'Destiny Raiment', 'A dazzling array of fabric and engineering layered over steel alloy. Despite the wars it has seen, it bears no scars.', 50000, 0, 200, 0, 50, 0, 0, 5, 5, ''),
|
||||||
|
(28, 3, 'Reed Shield', 'Stiff river reeds. Dried, cured, strapped together with twine.', 50, 0, 2, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(29, 3, 'Buckler', 'Planks of wood cleaned up and formed into a small, round shield.', 100, 0, 4, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(30, 3, 'Small Shield', 'Cedar is treated to withstand impacts, then bonded together with rivets.', 500, 0, 10, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(31, 3, 'Tower Shield', 'A very heavy, tall shield built to withstand might blows.', 2500, 0, 30, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(32, 3, 'Silver Shield', 'Surprisingly sturdy, this gleaming shield lets attacks bounce right off.', 10000, 0, 60, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(33, 3, 'Destiny Aegis', 'An exquisite piece of forging, it is surprisingly light. Impacts seem to be absorbed into nothing.', 25000, 0, 100, 0, 0, 50, 0, 5, 5, ''),
|
||||||
|
(34, 4, 'Life Pebble', 'Shiny green rock. Seems to put a spring in your step.', 0, 0, 0, 0, 0, 10, 0, 0, 0, ''),
|
||||||
|
(35, 4, 'Life Stone', 'Glistening green rock. You feel energized with it.', 0, 0, 0, 0, 0, 25, 0, 0, 0, ''),
|
||||||
|
(36, 4, 'Life Rock', 'Polished green rock. You feel as if you''ll live longer with it.', 0, 0, 0, 0, 0, 50, 0, 0, 0, ''),
|
||||||
|
(37, 4, 'Magic Pebble', 'Shiny blue rock. You feel at ease with it.', 0, 0, 0, 0, 0, 0, 10, 0, 0, ''),
|
||||||
|
(38, 4, 'Magic Stone', 'Glistening blue rock. It feels like it gives you clarity.', 0, 0, 0, 0, 0, 0, 25, 0, 0, ''),
|
||||||
|
(39, 4, 'Magic Rock', 'Polished blue rock. It seems to hum in your mind.', 0, 0, 0, 0, 0, 0, 50, 0, 0, ''),
|
||||||
|
(40, 4, 'Dragon''s Scale', 'A scale from a young dragon.', 0, 0, 25, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(41, 4, 'Dragon''s Plate', 'A piece of the ridge of an adult dragon.', 0, 0, 50, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(42, 4, 'Dragon''s Claw', 'A claw taken from a dragon.', 0, 25, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(43, 4, 'Dragon''s Tooth', 'A fang ripped from a dragon.', 0, 50, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(44, 4, 'Dragon''s Tear', 'A sad reminder of a dragon''s death.', 0, 0, 0, 50, 0, 0, 0, 0, 0, ''),
|
||||||
|
(45, 4, 'Dragon''s Wing', 'A flap of leather from a dragon''s wing.', 0, 0, 0, 0, 50, 0, 0, 0, 0, ''),
|
||||||
|
(46, 4, 'Demon''s Sin', 'The demons sin; sacrificing life for power.', 0, 0, 0, 50, 0, -50, 0, 0, 0, ''),
|
||||||
|
(47, 4, 'Demon''s Fall', 'The demons failure; sacrificing wisdom for strength.', 0, 0, 0, 50, 0, 0, -50, 0, 0, ''),
|
||||||
|
(48, 4, 'Demon''s Lie', 'The demons lie; power is more valuable than life.', 0, 0, 0, 100, 0, -100, 0, 0, 0, ''),
|
||||||
|
(49, 4, 'Demon''s Hate', 'The demons hate; the will of others.', 0, 0, 0, 100, 0, 0, -100, 0, 0, ''),
|
||||||
|
(50, 4, 'Angel''s Joy', 'The angels joy; the celebration of life.', 0, 0, 0, 25, 0, 25, 0, 0, 0, ''),
|
||||||
|
(51, 4, 'Angel''s Rise', 'The angels rise; growth begets strength.', 0, 0, 0, 50, 0, 50, 0, 0, 0, ''),
|
||||||
|
(52, 4, 'Angel''s Truth', 'The angels truth; all breath is valuable.', 0, 0, 0, 75, 0, 75, 0, 0, 0, ''),
|
||||||
|
(53, 4, 'Angel''s Love', 'The angels love; the protection of existence.', 0, 0, 0, 100, 0, 100, 0, 0, 0, ''),
|
||||||
|
(54, 4, 'Seraph''s Wisdom', 'The seraphs wisdom; meekness is strength.', 0, 0, 0, 0, 25, 0, 25, 0, 0, ''),
|
||||||
|
(55, 4, 'Seraph''s Strength', 'The seraphs strength; sharpness of mind and soul.', 0, 0, 0, 0, 50, 0, 50, 0, 0, ''),
|
||||||
|
(56, 4, 'Seraph''s Creed', 'The seraphs creed; the lust for power makes weak.', 0, 0, 0, 0, 75, 0, 75, 0, 0, ''),
|
||||||
|
(57, 4, 'Seraph''s Loyalty', 'The seraphs loyalty; to all creation.', 0, 0, 0, 0, 100, 0, 100, 0, 0, ''),
|
||||||
|
(58, 4, 'Ruby', 'A gorgeous red crystal.', 0, 0, 0, 0, 0, 150, 0, 0, 0, ''),
|
||||||
|
(59, 4, 'Pearl', 'A beautiful white crystal.', 0, 0, 0, 0, 0, 0, 150, 0, 0, ''),
|
||||||
|
(60, 4, 'Emerald', 'A mesmerizing green crystal.', 0, 0, 0, 150, 0, 0, 0, 0, 0, ''),
|
||||||
|
(61, 4, 'Topaz', 'A translucent orange crystal.', 0, 0, 0, 0, 150, 0, 0, 0, 0, ''),
|
||||||
|
(62, 4, 'Obsidian', 'A razor-sharp black crystal.', 0, 150, 0, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(63, 4, 'Diamond', 'A shockingly tough blue crystal.', 0, 0, 150, 0, 0, 0, 0, 0, 0, ''),
|
||||||
|
(64, 4, 'Memory Drop', 'This swirling blue liquid comes from the Fount.', 0, 0, 0, 0, 0, 0, 0, 10, 0, ''),
|
||||||
|
(65, 4, 'Fortune Drop', 'This mysterious green, sparkly concoction comes from lands unknown.', 0, 0, 0, 0, 0, 0, 0, 0, 10, '');
|
@ -64,6 +64,12 @@
|
|||||||
<li>Dexterity: {user.Dexterity}</li>
|
<li>Dexterity: {user.Dexterity}</li>
|
||||||
<li>Attack: {user.Attack}</li>
|
<li>Attack: {user.Attack}</li>
|
||||||
<li>Defense: {user.Defense}</li>
|
<li>Defense: {user.Defense}</li>
|
||||||
|
{if user.ExpBonus != 0}
|
||||||
|
<li>EXP Bonus: {user.ExpBonus}%</li>
|
||||||
|
{/if}
|
||||||
|
{if user.GoldBonus != 0}
|
||||||
|
<li>Gold Bonus: {user.GoldBonus}%</li>
|
||||||
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -36,8 +36,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{item.Att}
|
{if item.HasStatBonuses()}
|
||||||
{if item.Type == 1}Attack{else}Defense{/if}
|
<ul class="unstyled">
|
||||||
|
{for i,v in item.GetStatBonuses()}
|
||||||
|
<li>{i}: {v}{if i == "Gold Bonus" or i == "EXP Bonus"}%{/if}</li>
|
||||||
|
{/for}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{if user.WeaponID == item.ID or user.ArmorID == item.ID or user.ShieldID == item.ID}
|
{if user.WeaponID == item.ID or user.ArmorID == item.ID or user.ShieldID == item.ID}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user