Sfoglia il codice sorgente

Adds enums for gear and classes

It's commented who will have access to what (I'll make it so if you
can't use it, don't show it).

I think I'm ready now to begin developing the workings of the main
city... "North Folk".
Apollo 8 mesi fa
parent
commit
cc82fe2b9f
3 ha cambiato i file con 527 aggiunte e 38 eliminazioni
  1. 38 11
      db.go
  2. 31 27
      main.go
  3. 458 0
      types.go

+ 38 - 11
db.go

@@ -44,18 +44,18 @@ type User struct {
 	MaxHealth           uint32
 	Magic               uint32
 	MaxMagic            uint32
-	Class               uint8 // 1=Knight (Cyan), 2=Thief (Brown), 3=Cleric (Green), 4=Wizard (Magenta), 5=Ranger (Red)
+	Class               Class // K T C W R
 	Level               uint32
 	Xp                  uint64
-	Weapon              uint8
-	WeaponEnchant       uint8
-	Armor               uint8
-	ArmorEnchant        uint8
-	RangedWeapon        uint8
-	RangedWeaponEnchant uint8
-	Shield              uint8
-	Ring                uint8
-	Amulet              uint8
+	Weapon              Weapon
+	WeaponEnchant       WeaponEnchant
+	Armor               Armor
+	ArmorEnchant        ArmorEnchant
+	RangedWeapon        RangedWeapon
+	RangedWeaponEnchant RangedWeaponEnchant
+	Shield              Shield
+	Ring                Ring
+	Amulet              Amulet
 	Gold                uint64
 	GoldBanked          uint64
 	GemRed              uint32
@@ -80,6 +80,23 @@ func (u *UserDB) FindUserByName(name string) *User {
 	return usr
 }
 
+func (u *UserDB) FindUserById(id uint64) *User {
+	var usr *User
+	result := u.DB.First(&usr, id)
+	if result.Error != nil {
+		log.Printf("UserDB.FindUser(id=%d) > %v", id, result.Error)
+		return nil
+	}
+	// Hot load the users faction if they are in one
+	if usr != nil && usr.FactionId != 0 {
+		fac := u.FindFactionById(usr.FactionId)
+		if fac != nil {
+			usr.Faction = fac
+		}
+	}
+	return usr
+}
+
 func (u *User) SetPassword(password string) error {
 	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
 	if err != nil {
@@ -109,7 +126,7 @@ func (u *UserDB) SaveUser(usr *User) error {
 
 type Faction struct {
 	Model
-	Name        string `gorm:"unique,collate:nocase"`
+	Name        string `gorm:"unique,collate:no_case"`
 	Description string
 	LeaderId    uint64 `gorm:"unique"`
 	Gold        uint64
@@ -138,6 +155,16 @@ func (u *UserDB) FindFactionById(id uint64) *Faction {
 	return fac
 }
 
+func (u *UserDB) FindFactionByLeaderId(leader_id uint64) *Faction {
+	var fac *Faction
+	result := u.DB.First(&fac, &Faction{LeaderId: leader_id})
+	if result.Error != nil {
+		log.Printf("UserDB.FindFactionByLeaderId(leader_id=%d) > %v", leader_id, result.Error)
+		return nil
+	}
+	return fac
+}
+
 func (u *UserDB) SaveFaction(fac *Faction) error {
 	result := u.DB.Save(fac)
 	return result.Error

+ 31 - 27
main.go

@@ -58,11 +58,11 @@ LOGIN:
 			}
 			d.WriteS(door.Reset + door.CRNL)
 			bb := door.ColorTextS("BRI BLA ON BLA")
-			d.WriteS(bb + "[" + door.ColorTextS("BRI CYA ON BLA") + "K" + bb + "] " + door.ColorTextS("CYA ON BLA") + "Knight" + door.Reset + door.CRNL)
-			d.WriteS(bb + "[" + door.ColorTextS("BRO ON BLA") + "T" + bb + "] " + door.ColorTextS("BRO ON BLA") + "Thief" + door.Reset + door.CRNL)
-			d.WriteS(bb + "[" + door.ColorTextS("BRI GRE ON BLA") + "C" + bb + "] " + door.ColorTextS("GRE ON BLA") + "Cleric" + door.Reset + door.CRNL)
-			d.WriteS(bb + "[" + door.ColorTextS("BRI MAG ON BLA") + "W" + bb + "] " + door.ColorTextS("MAG ON BLA") + "Wizard" + door.Reset + door.CRNL)
-			d.WriteS(bb + "[" + door.ColorTextS("BRI RED ON BLA") + "R" + bb + "] " + door.ColorTextS("RED ON BLA") + "Ranger" + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS(C_KNIGHT.ColorText()) + "K" + bb + "] " + door.ColorTextS(C_KNIGHT.ColorText()) + C_KNIGHT.Name() + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS(C_THIEF.ColorText()) + "T" + bb + "] " + door.ColorTextS(C_THIEF.ColorText()) + C_THIEF.Name() + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS(C_CLERIC.ColorText()) + "C" + bb + "] " + door.ColorTextS(C_CLERIC.ColorText()) + C_CLERIC.Name() + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS(C_WIZARD.ColorText()) + "W" + bb + "] " + door.ColorTextS(C_WIZARD.ColorText()) + C_WIZARD.Name() + door.Reset + door.CRNL)
+			d.WriteS(bb + "[" + door.ColorTextS(C_RANGER.ColorText()) + "R" + bb + "] " + door.ColorTextS(C_RANGER.ColorText()) + C_RANGER.Name() + door.Reset + door.CRNL)
 			r, _, err := d.WaitKey(door.Inactivity)
 			if err != nil {
 				if errors.Is(err, door.ErrDisconnected) {
@@ -72,35 +72,35 @@ LOGIN:
 			}
 			switch r {
 			case 'k', 'K':
-				u.Class = 1
+				u.Class = C_KNIGHT
 				u.Health = 30
 				u.MaxHealth = 30
 				u.MaxMagic = 0
 				u.Magic = 0
 				u.Gold = 200
 			case 't', 'T':
-				u.Class = 2
+				u.Class = C_THIEF
 				u.Health = 20
 				u.MaxHealth = 20
 				u.MaxMagic = 0
 				u.Magic = 0
 				u.Gold = 250 // Thieves get bonus gold (+50)
 			case 'c', 'C':
-				u.Class = 3
+				u.Class = C_CLERIC
 				u.Health = 25
 				u.MaxHealth = 25
 				u.MaxMagic = 7
 				u.Magic = 7
 				u.Gold = 200
 			case 'w', 'W':
-				u.Class = 4
+				u.Class = C_WIZARD
 				u.Health = 20
 				u.MaxHealth = 20
 				u.MaxMagic = 12
 				u.Magic = 12
 				u.Gold = 200
 			case 'r', 'R':
-				u.Class = 5
+				u.Class = C_RANGER
 				u.Health = 20
 				u.MaxHealth = 20
 				u.MaxMagic = 0
@@ -139,26 +139,19 @@ LOGIN:
 		}
 	}
 	d.WriteS(door.ColorTextS("BRI CYA ON BLA") + "Welcome " + door.ColorTextS("BRI WHI ON BLA") + u.Name + door.ColorTextS("BRI CYA ON BLA") + " the ")
-	switch u.Class {
-	case 1:
-		d.WriteS(door.ColorTextS("CYA ON BLA") + "Knight")
-	case 2:
-		d.WriteS(door.ColorTextS("BRO ON BLA") + "Thief")
-	case 3:
-		d.WriteS(door.ColorTextS("GRE ON BLA") + "Cleric")
-	case 4:
-		d.WriteS(door.ColorTextS("MAG ON BLA") + "Wizard")
-	case 5:
-		d.WriteS(door.ColorTextS("RED ON BLA") + "Ranger")
-	default:
-		d.WriteS(door.ColorTextS("BLI BRI RED ON BLA") + "Unknown")
-	}
+	d.WriteS(door.ColorTextS(u.Class.ColorText()) + u.Class.Name())
 	d.WriteS(door.Reset + door.CRNL)
-	go renderStatusBar()
+	go renderStatusBar("Hello "+u.Name, time.Now().Format("Jan 02 2006 @ 03:04:05PM"))
+	d.WriteS(fmt.Sprintf("Weapon: %s with %s enchantment", u.Weapon.Name(), u.WeaponEnchant.Name()) + door.CRNL)
+	d.WriteS(fmt.Sprintf("Armor:  %s with %s enchantment", u.Armor.Name(), u.ArmorEnchant.Name()) + door.CRNL)
+	d.WriteS(fmt.Sprintf("Ranged: %s with %s enchantment", u.RangedWeapon.Name(), u.RangedWeaponEnchant.Name()) + door.CRNL)
+	d.WriteS(fmt.Sprintf("Shield: %s", u.Shield.Name()) + door.CRNL)
+	d.WriteS(fmt.Sprintf("Ring:   %s", u.Ring.Name()) + door.CRNL)
+	d.WriteS(fmt.Sprintf("Amulet: %s", u.Amulet.Name()) + door.CRNL)
 	d.WaitKey(door.Inactivity)
 }
 
-func renderStatusBar() {
+func renderStatusBar(msg ...string) {
 	if d.Disconnected || u == nil {
 		return
 	}
@@ -167,7 +160,18 @@ func renderStatusBar() {
 	bb := door.ColorTextS("BRI BLA ON BLA")
 	bar_width := ((door.Width - 3) / 2) - 1
 	hp := int(float64(bar_width) * (float64(u.Health) / float64(u.MaxHealth)))
-	pos_txt := fmt.Sprintf("%-3d%% (%d, %d) %-3d%%", int((float64(u.Health)/float64(u.MaxHealth))*100.0), u.X, u.Y, int((float64(u.Magic)/float64(u.MaxMagic))*100.0))
+	var pos_txt string
+	if len(msg) >= 1 {
+		pos_txt += msg[0] + " - "
+	}
+	if u.MaxMagic != 0 {
+		pos_txt += fmt.Sprintf("%-3d%% (%d, %d) %-3d%%", int((float64(u.Health)/float64(u.MaxHealth))*100.0), u.X, u.Y, int((float64(u.Magic)/float64(u.MaxMagic))*100.0))
+	} else {
+		pos_txt += fmt.Sprintf("%-3d%% (%d, %d)", int((float64(u.Health)/float64(u.MaxHealth))*100.0), u.X, u.Y)
+	}
+	if len(msg) >= 2 {
+		pos_txt += " - " + msg[1]
+	}
 	d.WriteS(door.SavePos + door.GotoS((door.Width/2)-(len(pos_txt)/2), door.Height-1) + door.ColorTextS("BRI CYA ON BLA") + pos_txt + door.RestorePos)
 	if u.MaxMagic != 0 {
 		mp := int(float64(bar_width) * (float64(u.Magic) / float64(u.MaxMagic)))

+ 458 - 0
types.go

@@ -0,0 +1,458 @@
+package main
+
+type Class uint8
+
+const (
+	C_NONE Class = iota
+	C_KNIGHT
+	C_THIEF
+	C_CLERIC
+	C_WIZARD
+	C_RANGER
+)
+
+func (c Class) Name() string {
+	switch c {
+	case C_KNIGHT:
+		return "Knight"
+	case C_THIEF:
+		return "Thief"
+	case C_CLERIC:
+		return "Cleric"
+	case C_WIZARD:
+		return "Wizard"
+	case C_RANGER:
+		return "Ranger"
+	default:
+		return "None"
+	}
+}
+
+func (c Class) ColorText() string {
+	switch c {
+	case C_KNIGHT:
+		return "CYA ON BLA"
+	case C_THIEF:
+		return "BRO ON BLA"
+	case C_CLERIC:
+		return "GRE ON BLA"
+	case C_WIZARD:
+		return "MAG ON BLA"
+	case C_RANGER:
+		return "BRI RED ON BLA"
+	default:
+		return "WHI ON BLA"
+	}
+}
+
+type Weapon uint8
+
+const (
+	W_FIST    Weapon = iota // K T C W R | Damage 1
+	W_DAGGER                // K T C W R | Damage 3
+	W_WHIP                  // K T C _ R | Damage 5
+	W_SWORD                 // K T _ _ R | Damage 8
+	W_Q_STAFF               // _ _ C W _ | Damage 7
+	W_MACE                  // K _ C _ _ | Damage 10
+	W_L_SWORD               // K _ _ _ R | Damage 12
+	W_HAMMER                // K _ C _ _ | Damage 16
+	W_B_SWORD               // K _ _ _ _ | Damage 18
+)
+
+func (w Weapon) Name() string {
+	switch w {
+	case W_DAGGER:
+		return "Dagger"
+	case W_WHIP:
+		return "Whip"
+	case W_SWORD:
+		return "Short Sword"
+	case W_Q_STAFF:
+		return "Quarter Staff"
+	case W_MACE:
+		return "Mace"
+	case W_L_SWORD:
+		return "Long Sword"
+	case W_HAMMER:
+		return "Hammer"
+	case W_B_SWORD:
+		return "Bastard Sword"
+	default:
+		return "Fist"
+	}
+}
+
+type WeaponEnchant uint8
+
+const ( // Unless stated, All Weapon enchants are Red gems
+	WE_NONE      WeaponEnchant = iota // K T C W R
+	WE_DAMAGE                         // K T C W R | Damage +2
+	WE_DAMAGE2                        // K T C W R | Damage +3
+	WE_DAMAGE3                        // K T C W R | Damage +5
+	WE_POISON                         // K T _ _ R | G | DOT 1 for 4 turns
+	WE_POISON2                        // _ T _ _ _ | G | Dot 1-3 for 5 turns
+	WE_FIRE                           // K _ C _ R | Fire DOT 1-2 for 2 turns
+	WE_FIRE2                          // K _ C _ R | Fire DOT 2-6 for 2 turns (35% chance)
+	WE_ICE                            // K T C W R | B | 5% chance to Freeze target for 1 turn
+	WE_ICE2                           // K T C W R | B | 10% chance to Freeze target for 1-2 turn(s)
+	WE_ICE3                           // K T C W R | B | 20% chance to Freeze target for 2 turns
+	WE_STEAL_HP                       // K T C W R | Steals 1-2 HP (We get, they take)
+	WE_STEAL_MP                       // _ _ C W _ | Steals 1-2 MP (We get, they take)
+	WE_GOLDEN                         // K T C W R | +5% Gold
+	WE_GOLDEN2                        // _ T _ _ _ | +10% Gold
+	WE_XP                             // K T C W R | +5% Xp
+	WE_XP2                            // K T C W R | +10% Xp
+	WE_D_STRIKE                       // K T C _ R | 15% chance
+	WE_D_STRIKE2                      // K T C _ R | 25% chance
+)
+
+func (we WeaponEnchant) Name() string {
+	switch we {
+	case WE_DAMAGE:
+		return "Sharpened I"
+	case WE_DAMAGE2:
+		return "Sharpened II"
+	case WE_DAMAGE3:
+		return "Sharpened III"
+	case WE_POISON:
+		return "Poisonous I"
+	case WE_POISON2:
+		return "Poisonous II"
+	case WE_FIRE:
+		return "Flaming I"
+	case WE_FIRE2:
+		return "Flaming II"
+	case WE_ICE:
+		return "Freezing I"
+	case WE_ICE2:
+		return "Freezing II"
+	case WE_ICE3:
+		return "Freezing III"
+	case WE_STEAL_HP:
+		return "Vampiric I"
+	case WE_STEAL_MP:
+		return "Siphon I"
+	case WE_GOLDEN:
+		return "Golden I"
+	case WE_GOLDEN2:
+		return "Golden II"
+	case WE_XP:
+		return "Experience I"
+	case WE_XP2:
+		return "Experience II"
+	case WE_D_STRIKE:
+		return "Double Strike I"
+	case WE_D_STRIKE2:
+		return "Double Strike II"
+	default:
+		return "None"
+	}
+}
+
+type RangedWeapon uint8
+
+const (
+	RW_NONE      RangedWeapon = iota // K T C W R
+	RW_SLING                         // K T C W R | Damage 1
+	RW_S_BOW                         // K T C _ R | Damage 3
+	RW_L_C_BOW                       // K T C _ R | Damage 6
+	RW_L_BOW                         // _ T _ _ R | Damage 10
+	RW_H_C_BOW                       // _ _ _ _ R | Damage 12
+	RW_L_R_C_BOW                     // _ T _ _ R | Damage 15
+	RW_H_R_C_BOW                     // _ _ _ _ R | Damage 18
+)
+
+func (rw RangedWeapon) Name() string {
+	switch rw {
+	case RW_SLING:
+		return "Sling"
+	case RW_S_BOW:
+		return "Short Bow"
+	case RW_L_C_BOW:
+		return "Light Cross-Bow"
+	case RW_L_BOW:
+		return "Long Bow"
+	case RW_H_C_BOW:
+		return "Heavy Cross-Bow"
+	case RW_L_R_C_BOW:
+		return "Light Repeating Cross-Bow"
+	case RW_H_R_C_BOW:
+		return "Heavy Repeating Cross-Bow"
+	default:
+		return "None"
+	}
+}
+
+type RangedWeaponEnchant uint8
+
+const (
+	RWE_NONE    RangedWeaponEnchant = iota // _ T _ _ R
+	RWE_DAMAGE                             // _ T _ _ R | R | Damage +1
+	RWE_DAMAGE2                            // _ T _ _ R | R | Damage +2
+	RWE_DAMAGE3                            // _ _ _ _ R | R | Damage +4
+	RWE_POISON                             // _ T _ _ R | G | DOT 1-3 for 2 turns (40% chance)
+	RWE_POISON2                            // _ _ _ _ R | G | DOT 2-6 for 2 turns (30% chance, R get 40% chance)
+	RWE_FIRE                               // _ T _ _ R | R | Fire DOT 1 for 3 turns (50% chance)
+	RWE_FIRE2                              // _ T _ _ R | R | Fire DOT 1-2 for 3 turns (45% chance)
+	RWE_FIRE3                              // _ _ _ _ R | R | Fire DOT 1-2 for 6 turns (40% chance)
+	RWE_SLOW                               // _ T _ _ R | B | 35% chance, slows target by 50% for 4 turns
+	RWE_SLOW2                              // _ _ _ _ R | B | 50% chance, slows target by 50% for 4 turns
+	RWE_BLAST                              // _ _ _ _ R | R | 5% chance to cause target to explode dealing 5-10 damage (target then is immune for 10 turns)
+	RWE_D_GOLD                             // _ T _ _ R | B | 15% chance, double gold from kill (assuming kill shot, and gold was given)
+	RWE_D_XP                               // _ T _ _ R | B | 15% chance, double xp from kill (assuming kill shot, and xp was given)
+)
+
+func (rwe RangedWeaponEnchant) Name() string {
+	switch rwe {
+	case RWE_DAMAGE:
+		return "Sharp Arrows I"
+	case RWE_DAMAGE2:
+		return "Sharp Arrows II"
+	case RWE_DAMAGE3:
+		return "Sharp Arrows III"
+	case RWE_POISON:
+		return "Poison Arrows I"
+	case RWE_POISON2:
+		return "Poison Arrows II"
+	case RWE_FIRE:
+		return "Fire Arrows I"
+	case RWE_FIRE2:
+		return "Fire Arrows II"
+	case RWE_FIRE3:
+		return "Fire Arrows III"
+	case RWE_SLOW:
+		return "Ice Arrows I"
+	case RWE_SLOW2:
+		return "Ice Arrows II"
+	case RWE_BLAST:
+		return "Explosive Arrows I"
+	case RWE_D_GOLD:
+		return "Double Gold I"
+	case RWE_D_XP:
+		return "Double Xp I"
+	default:
+		return "None"
+	}
+}
+
+type Armor uint8
+
+const ( // T4 & T5 needs all gem types
+	A_CLOTHES       Armor = iota // K T C W R | T0 | Default Armor (No Bonuses)
+	A_CLOTH_MAIL                 // K _ C _ _ | T1 | +1 Melee, K: +5 HP, C: +1 Heal
+	A_ROBE                       // _ _ C W _ | T1 | +5 MP, +1 Magic, C: +1 Heal
+	A_LEATHER                    // _ T _ _ R | T1 | T: +10% Dodge, R: +1 Ranged
+	A_PLATE_MAIL                 // K _ C _ _ | T2 | +2 Melee, K: +10 HP, C: +1 Heal
+	A_G_ROBE                     // _ _ C W _ | T2 | +10 MP, +2 Magic, C: +2 Heal
+	A_R_LEATHER                  // _ T _ _ R | T2 | T: +20% Dodge, R: +2 Ranged
+	A_SCALE_MAIL                 // K _ C _ _ | T3 | +3 Melee, K: +15 HP, C: +1 Heal
+	A_M_ROBE                     // _ _ C W _ | T3 | +15 MP, +3 Magic, C: +3 Heal
+	A_PATCH_LEATHER              // _ T _ _ R | T3 | T: +30% Dodge, R: +3 Ranged
+	A_C_MAIL                     // K _ C _ _ | T4 | +4 Melee, K: +20 HP, C: +2 Heal
+	A_C_ROBE                     // _ _ C W _ | T4 | +20 MP, +4 Magic, C: +4 Heal
+	A_C_LEATHER                  // _ T _ _ R | T4 | T: +40% Dodge, R: +4 Ranged
+	A_D_MAIL                     // K _ C _ _ | T5 | +5 Melee, K: +20 HP (Cap Reached), C: +2 Heal
+	A_D_ROBE                     // _ _ C W _ | T5 | +20 MP (Cap Reached), +5 Magic, C: +5 Heal
+	A_D_LEATHER                  // _ T _ _ R | T5 | T: +50% Dodge, R: +5 Ranged
+)
+
+func (a Armor) Name() string {
+	switch a {
+	case A_CLOTHES:
+		return "Clothes"
+	case A_CLOTH_MAIL:
+		return "Cloth Mail"
+	case A_ROBE:
+		return "Robe"
+	case A_LEATHER:
+		return "Leather"
+	case A_PLATE_MAIL:
+		return "Plate Mail"
+	case A_G_ROBE:
+		return "Grand Robe"
+	case A_R_LEATHER:
+		return "Reinforced Leather"
+	case A_SCALE_MAIL:
+		return "Scale Mail"
+	case A_M_ROBE:
+		return "Masters Robe"
+	case A_PATCH_LEATHER:
+		return "Patch Leather"
+	case A_C_MAIL:
+		return "Crystal Mail"
+	case A_C_ROBE:
+		return "Crystal Robe"
+	case A_C_LEATHER:
+		return "Crystal Leather"
+	case A_D_MAIL:
+		return "Dragon Mail"
+	case A_D_ROBE:
+		return "Dragon Robe"
+	case A_D_LEATHER:
+		return "Dragon Leather"
+	default:
+		return "Unknown"
+	}
+}
+
+type ArmorEnchant uint8
+
+// No need for +MP every X turns as that's already done (+(1 per 3 levels) MP every 4 turns)
+
+const ( // Red gems are used for Damage, Blue for Protections, and Green for Stat boosts (Health and Magic) and Health Regeneration
+	AE_NONE      ArmorEnchant = iota // K T C W R
+	AE_HEAL                          // K T C W R | G | +1 HP every 8 turns (R's have 'Natures Healing' +1 HP every 8 turns, this essentially can double it)
+	AE_HEALTH                        // K T C W R | G | +5 HP
+	AE_HEALTH2                       // K _ C _ R | G | +10 HP
+	AE_MAGIC                         // _ _ C W _ | G | +5 MP
+	AE_MAGIC2                        // _ _ C W _ | G | +10 MP
+	AE_SPIKE                         // K T C W R | R | 1 Damage to attackers
+	AE_BLAST                         // K _ C W R | R | Explode dealing 5-10 damage around you, triggers at 75%, 50% and 25% health, 5 turn delay between blasts, must reach 100% health to reset
+	AE_M_PROTECT                     // K T C W R | B | -10% chance to hit (Melee)
+	AE_R_PROTECT                     // K T C W R | B | -30% chance to hit (Ranged)
+	AE_IM_FIRE                       // K T C W R | R | 15% chance, Blocks Fire Damage (This includes Blast)
+	AE_IM_COLD                       // K T C W R | B | 15% chance, Blocks Cold Damage (This includes Sleep)
+	AE_IM_ACID                       // K T C W R | G | 15% chance, Blocks Acid Damage (This includes Poison)
+)
+
+func (ae ArmorEnchant) Name() string {
+	switch ae {
+	case AE_HEAL:
+		return "Regenerative I"
+	case AE_HEALTH:
+		return "Healthy I"
+	case AE_HEALTH2:
+		return "Healthy II"
+	case AE_MAGIC:
+		return "Magical I"
+	case AE_MAGIC2:
+		return "Magical II"
+	case AE_SPIKE:
+		return "Spiked I"
+	case AE_BLAST:
+		return "Blast I"
+	case AE_M_PROTECT:
+		return "Melee Protective I"
+	case AE_R_PROTECT:
+		return "Ranged Protective I"
+	case AE_IM_FIRE:
+		return "Fire Immune I"
+	case AE_IM_COLD:
+		return "Cold Immune I"
+	case AE_IM_ACID:
+		return "Acid Immune I"
+	default:
+		return "None"
+	}
+}
+
+type Shield uint8
+
+const (
+	S_NONE    Shield = iota // K T C W R
+	S_BUCKLER               // K T C _ R | +5% Block
+	S_SMALL                 // K _ C _ R | +10% Block
+	S_MEDIUM                // K _ C _ _ | +12% Block
+	S_LARGE                 // K _ C _ _ | +15% Block
+	S_TOWER                 // K _ _ _ _ | +20% Block
+)
+
+func (s Shield) Name() string {
+	switch s {
+	case S_BUCKLER:
+		return "Buckler"
+	case S_SMALL:
+		return "Small"
+	case S_MEDIUM:
+		return "Medium"
+	case S_LARGE:
+		return "Large"
+	case S_TOWER:
+		return "Tower"
+	default:
+		return "None"
+	}
+}
+
+type Ring uint8
+
+const (
+	R_NONE    Ring = iota // K T C W R
+	R_HEALTH              // K T C W R | +5 HP
+	R_HEALTH2             // K T C W R | +10 HP
+	R_MAGIC               // K T C W R | +5 MP
+	R_MAGIC2              // K T C W R | +10 MP
+	R_MELEE               // K T C W R | +1 Melee
+	R_RANGED              // K T C W R | +1 Ranged
+	R_UP_FIRE             // K T C W R | +1 Fire
+	R_UP_COLD             // K T C W R | +1 Cold
+	R_UP_ACID             // K T C W R | +1 Acid
+	R_IM_FIRE             // K T C W R | +10% chance, Blocks Fire Damage
+	R_IM_COLD             // K T C W R | +10% chance, Blocks Cold Damage
+	R_IM_ACID             // K T C W R | +10% chance, Blocks Acid Damage
+)
+
+func (r Ring) Name() string {
+	switch r {
+	case R_HEALTH:
+		return "Lesser Health"
+	case R_HEALTH2:
+		return "Greater Health"
+	case R_MAGIC:
+		return "Lesser Magic"
+	case R_MAGIC2:
+		return "Greater Magic"
+	case R_MELEE:
+		return "Melee"
+	case R_RANGED:
+		return "Ranged"
+	case R_UP_FIRE:
+		return "Warmth"
+	case R_UP_COLD:
+		return "Cooling"
+	case R_UP_ACID:
+		return "Acidic"
+	case R_IM_FIRE:
+		return "Fire Immune"
+	case R_IM_COLD:
+		return "Cold Immune"
+	case R_IM_ACID:
+		return "Acid Immune"
+	default:
+		return "None"
+	}
+}
+
+type Amulet uint8
+
+const (
+	AM_NONE    Amulet = iota // K T C W R
+	AM_HEALTH                // K T C W R | +5 HP
+	AM_MAGIC                 // K T C W R | +5 MP
+	AM_MELEE                 // K T C W R | +1 Melee
+	AM_RANGED                // K T C W R | +1 Ranged
+	AM_IM_FIRE               // K T C W R | +10% chance, Blocks Fire Damage
+	AM_IM_COLD               // K T C W R | +10% chance, Blocks Cold Damage
+	AM_IM_ACID               // K T C W R | +10% chance, Blocks Acid Damage
+)
+
+func (am Amulet) Name() string {
+	switch am {
+	case AM_HEALTH:
+		return "Health"
+	case AM_MAGIC:
+		return "Magic"
+	case AM_MELEE:
+		return "Melee"
+	case AM_RANGED:
+		return "Ranged"
+	case AM_IM_FIRE:
+		return "Fire Immune"
+	case AM_IM_COLD:
+		return "Cold Immune"
+	case AM_IM_ACID:
+		return "Acid Immune"
+	default:
+		return "None"
+	}
+}