Decompose things.go (refactor step 7)
inventoryName splits into nameScroll/nameFood/nameWeapon/nameArmor/ describeWorn/fixNameCase; newThing gains newFoodThing/newWeaponThing/ newArmorThing/newRingThing; dropCheck gains dropRing; addLine splits into addLineSlow/addLinePaged/addLineOverlay. things.go is complexity-clean. Behavior and RNG call order unchanged.
This commit is contained in:
484
game/things.go
484
game/things.go
@@ -23,107 +23,147 @@ func (g *RogueGame) inventoryName(obj *Object, drop bool) string {
|
||||
case KindWand:
|
||||
g.nameit(&pb, obj, it.WandType[which], it.WandMade[which], &it.Sticks[which], chargeStr)
|
||||
case KindScroll:
|
||||
if obj.Count == 1 {
|
||||
pb.WriteString("A scroll ")
|
||||
} else {
|
||||
fmt.Fprintf(&pb, "%d scrolls ", obj.Count)
|
||||
}
|
||||
|
||||
op := &it.Scrolls[which]
|
||||
|
||||
switch {
|
||||
case op.Know:
|
||||
fmt.Fprintf(&pb, "of %s", op.Name)
|
||||
case op.Guess != "":
|
||||
fmt.Fprintf(&pb, "called %s", op.Guess)
|
||||
default:
|
||||
fmt.Fprintf(&pb, "titled '%s'", it.ScrNames[which])
|
||||
}
|
||||
g.nameScroll(&pb, obj)
|
||||
case KindFood:
|
||||
if which == 1 {
|
||||
if obj.Count == 1 {
|
||||
fmt.Fprintf(&pb, "A%s %s", vowelstr(g.Fruit), g.Fruit)
|
||||
} else {
|
||||
fmt.Fprintf(&pb, "%d %ss", obj.Count, g.Fruit)
|
||||
}
|
||||
} else {
|
||||
if obj.Count == 1 {
|
||||
pb.WriteString("Some food")
|
||||
} else {
|
||||
fmt.Fprintf(&pb, "%d rations of food", obj.Count)
|
||||
}
|
||||
}
|
||||
g.nameFood(&pb, obj)
|
||||
case KindWeapon:
|
||||
sp := it.Weapons[which].Name
|
||||
|
||||
if obj.Count > 1 {
|
||||
fmt.Fprintf(&pb, "%d ", obj.Count)
|
||||
} else {
|
||||
fmt.Fprintf(&pb, "A%s ", vowelstr(sp))
|
||||
}
|
||||
|
||||
if obj.Flags.Has(Known) {
|
||||
fmt.Fprintf(&pb, "%s %s", num(obj.HPlus, obj.DPlus, Weapon), sp)
|
||||
} else {
|
||||
pb.WriteString(sp)
|
||||
}
|
||||
|
||||
if obj.Count > 1 {
|
||||
pb.WriteString("s")
|
||||
}
|
||||
|
||||
if obj.Label != "" {
|
||||
fmt.Fprintf(&pb, " called %s", obj.Label)
|
||||
}
|
||||
g.nameWeapon(&pb, obj)
|
||||
case KindArmor:
|
||||
sp := it.Armors[which].Name
|
||||
if obj.Flags.Has(Known) {
|
||||
fmt.Fprintf(&pb, "%s %s [", num(g.data.aClass[which]-obj.ArmorClass, 0, Armor), sp)
|
||||
|
||||
if !g.Options.Terse {
|
||||
pb.WriteString("protection ")
|
||||
}
|
||||
|
||||
fmt.Fprintf(&pb, "%d]", 10-obj.ArmorClass)
|
||||
} else {
|
||||
pb.WriteString(sp)
|
||||
}
|
||||
|
||||
if obj.Label != "" {
|
||||
fmt.Fprintf(&pb, " called %s", obj.Label)
|
||||
}
|
||||
g.nameArmor(&pb, obj)
|
||||
case KindAmulet:
|
||||
pb.WriteString("The Amulet of Yendor")
|
||||
case KindGold:
|
||||
fmt.Fprintf(&pb, "%d Gold pieces", obj.GoldValue)
|
||||
}
|
||||
|
||||
out := pb.String()
|
||||
return fixNameCase(g.describeWorn(obj, pb.String()), drop)
|
||||
}
|
||||
|
||||
if g.InvDescribe {
|
||||
p := &g.Player
|
||||
if obj == p.CurArmor {
|
||||
out += " (being worn)"
|
||||
}
|
||||
|
||||
if obj == p.CurWeapon {
|
||||
out += " (weapon in hand)"
|
||||
}
|
||||
|
||||
switch obj {
|
||||
case p.CurRing[Left]:
|
||||
out += " (on left hand)"
|
||||
case p.CurRing[Right]:
|
||||
out += " (on right hand)"
|
||||
}
|
||||
// nameScroll writes a scroll's inventory name (things.c inv_name).
|
||||
func (g *RogueGame) nameScroll(pb *strings.Builder, obj *Object) {
|
||||
if obj.Count == 1 {
|
||||
pb.WriteString("A scroll ")
|
||||
} else {
|
||||
fmt.Fprintf(pb, "%d scrolls ", obj.Count)
|
||||
}
|
||||
|
||||
if out != "" {
|
||||
if drop && isUpper(out[0]) {
|
||||
out = string(toLower(out[0])) + out[1:]
|
||||
} else if !drop && isLower(out[0]) {
|
||||
out = string(toUpper(out[0])) + out[1:]
|
||||
op := &g.Items.Scrolls[obj.Which]
|
||||
|
||||
switch {
|
||||
case op.Know:
|
||||
fmt.Fprintf(pb, "of %s", op.Name)
|
||||
case op.Guess != "":
|
||||
fmt.Fprintf(pb, "called %s", op.Guess)
|
||||
default:
|
||||
fmt.Fprintf(pb, "titled '%s'", g.Items.ScrNames[obj.Which])
|
||||
}
|
||||
}
|
||||
|
||||
// nameFood writes a food item's inventory name; which 1 is the fruit
|
||||
// (things.c inv_name).
|
||||
func (g *RogueGame) nameFood(pb *strings.Builder, obj *Object) {
|
||||
if obj.Which == 1 {
|
||||
if obj.Count == 1 {
|
||||
fmt.Fprintf(pb, "A%s %s", vowelstr(g.Fruit), g.Fruit)
|
||||
} else {
|
||||
fmt.Fprintf(pb, "%d %ss", obj.Count, g.Fruit)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if obj.Count == 1 {
|
||||
pb.WriteString("Some food")
|
||||
} else {
|
||||
fmt.Fprintf(pb, "%d rations of food", obj.Count)
|
||||
}
|
||||
}
|
||||
|
||||
// nameWeapon writes a weapon's inventory name (things.c inv_name).
|
||||
func (g *RogueGame) nameWeapon(pb *strings.Builder, obj *Object) {
|
||||
sp := g.Items.Weapons[obj.Which].Name
|
||||
|
||||
if obj.Count > 1 {
|
||||
fmt.Fprintf(pb, "%d ", obj.Count)
|
||||
} else {
|
||||
fmt.Fprintf(pb, "A%s ", vowelstr(sp))
|
||||
}
|
||||
|
||||
if obj.Flags.Has(Known) {
|
||||
fmt.Fprintf(pb, "%s %s", num(obj.HPlus, obj.DPlus, Weapon), sp)
|
||||
} else {
|
||||
pb.WriteString(sp)
|
||||
}
|
||||
|
||||
if obj.Count > 1 {
|
||||
pb.WriteString("s")
|
||||
}
|
||||
|
||||
if obj.Label != "" {
|
||||
fmt.Fprintf(pb, " called %s", obj.Label)
|
||||
}
|
||||
}
|
||||
|
||||
// nameArmor writes an armor's inventory name (things.c inv_name).
|
||||
func (g *RogueGame) nameArmor(pb *strings.Builder, obj *Object) {
|
||||
sp := g.Items.Armors[obj.Which].Name
|
||||
if obj.Flags.Has(Known) {
|
||||
fmt.Fprintf(pb, "%s %s [",
|
||||
num(g.data.aClass[obj.Which]-obj.ArmorClass, 0, Armor), sp)
|
||||
|
||||
if !g.Options.Terse {
|
||||
pb.WriteString("protection ")
|
||||
}
|
||||
|
||||
fmt.Fprintf(pb, "%d]", 10-obj.ArmorClass)
|
||||
} else {
|
||||
pb.WriteString(sp)
|
||||
}
|
||||
|
||||
if obj.Label != "" {
|
||||
fmt.Fprintf(pb, " called %s", obj.Label)
|
||||
}
|
||||
}
|
||||
|
||||
// describeWorn appends the equipped-status notes to an inventory name
|
||||
// (things.c inv_name).
|
||||
func (g *RogueGame) describeWorn(obj *Object, out string) string {
|
||||
if !g.InvDescribe {
|
||||
return out
|
||||
}
|
||||
|
||||
p := &g.Player
|
||||
if obj == p.CurArmor {
|
||||
out += " (being worn)"
|
||||
}
|
||||
|
||||
if obj == p.CurWeapon {
|
||||
out += " (weapon in hand)"
|
||||
}
|
||||
|
||||
switch obj {
|
||||
case p.CurRing[Left]:
|
||||
out += " (on left hand)"
|
||||
case p.CurRing[Right]:
|
||||
out += " (on right hand)"
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// fixNameCase upper- or lowercases the leading letter to suit the
|
||||
// sentence it will land in (things.c inv_name).
|
||||
func fixNameCase(out string, drop bool) string {
|
||||
if out == "" {
|
||||
return out
|
||||
}
|
||||
|
||||
if drop && isUpper(out[0]) {
|
||||
return string(toLower(out[0])) + out[1:]
|
||||
}
|
||||
|
||||
if !drop && isLower(out[0]) {
|
||||
return string(toUpper(out[0])) + out[1:]
|
||||
}
|
||||
|
||||
return out
|
||||
@@ -192,25 +232,33 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
|
||||
|
||||
p.CurArmor = nil
|
||||
default:
|
||||
hand := Right
|
||||
if obj == p.CurRing[Left] {
|
||||
hand = Left
|
||||
}
|
||||
|
||||
p.CurRing[hand] = nil
|
||||
|
||||
switch obj.RingKind() {
|
||||
case RingAddStrength:
|
||||
g.changeStrength(-obj.Bonus)
|
||||
case RingSeeInvisible:
|
||||
g.unsee(0)
|
||||
g.Extinguish(DUnsee)
|
||||
}
|
||||
g.dropRing(obj)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// dropRing takes a worn ring off with its side effects (things.c
|
||||
// dropcheck).
|
||||
func (g *RogueGame) dropRing(obj *Object) {
|
||||
p := &g.Player
|
||||
|
||||
hand := Right
|
||||
if obj == p.CurRing[Left] {
|
||||
hand = Left
|
||||
}
|
||||
|
||||
p.CurRing[hand] = nil
|
||||
|
||||
switch obj.RingKind() {
|
||||
case RingAddStrength:
|
||||
g.changeStrength(-obj.Bonus)
|
||||
case RingSeeInvisible:
|
||||
g.unsee(0)
|
||||
g.Extinguish(DUnsee)
|
||||
}
|
||||
}
|
||||
|
||||
// newThing returns a new random thing for the dungeon (things.c new_thing).
|
||||
func (g *RogueGame) newThing() *Object {
|
||||
cur := newObject()
|
||||
@@ -236,47 +284,13 @@ func (g *RogueGame) newThing() *Object {
|
||||
cur.Kind = KindScroll
|
||||
cur.Which = pickOne(g, g.Items.Scrolls[:])
|
||||
case 2:
|
||||
cur.Kind = KindFood
|
||||
|
||||
g.Player.NoFood = 0
|
||||
if g.rnd(10) != 0 {
|
||||
cur.Which = 0
|
||||
} else {
|
||||
cur.Which = 1
|
||||
}
|
||||
g.newFoodThing(cur)
|
||||
case 3:
|
||||
g.initWeapon(cur, WeaponKind(pickOne(g, g.Items.Weapons[:NumWeaponTypes])))
|
||||
|
||||
if r := g.rnd(100); r < 10 {
|
||||
cur.Flags.Set(Cursed)
|
||||
cur.HPlus -= g.rnd(3) + 1
|
||||
} else if r < 15 {
|
||||
cur.HPlus += g.rnd(3) + 1
|
||||
}
|
||||
g.newWeaponThing(cur)
|
||||
case 4:
|
||||
cur.Kind = KindArmor
|
||||
cur.Which = pickOne(g, g.Items.Armors[:])
|
||||
|
||||
cur.ArmorClass = g.data.aClass[cur.Which]
|
||||
if r := g.rnd(100); r < 20 {
|
||||
cur.Flags.Set(Cursed)
|
||||
cur.ArmorClass += g.rnd(3) + 1
|
||||
} else if r < 28 {
|
||||
cur.ArmorClass -= g.rnd(3) + 1
|
||||
}
|
||||
g.newArmorThing(cur)
|
||||
case 5:
|
||||
cur.Kind = KindRing
|
||||
|
||||
cur.Which = pickOne(g, g.Items.Rings[:])
|
||||
switch cur.RingKind() {
|
||||
case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
|
||||
if cur.Bonus = g.rnd(3); cur.Bonus == 0 {
|
||||
cur.Bonus = -1
|
||||
cur.Flags.Set(Cursed)
|
||||
}
|
||||
case RingAggravateMonsters, RingTeleportation:
|
||||
cur.Flags.Set(Cursed)
|
||||
}
|
||||
g.newRingThing(cur)
|
||||
case 6:
|
||||
cur.Kind = KindWand
|
||||
cur.Which = pickOne(g, g.Items.Sticks[:])
|
||||
@@ -286,6 +300,63 @@ func (g *RogueGame) newThing() *Object {
|
||||
return cur
|
||||
}
|
||||
|
||||
// newFoodThing rolls food, one time in ten the fruit (things.c
|
||||
// new_thing).
|
||||
func (g *RogueGame) newFoodThing(cur *Object) {
|
||||
cur.Kind = KindFood
|
||||
|
||||
g.Player.NoFood = 0
|
||||
if g.rnd(10) != 0 {
|
||||
cur.Which = 0
|
||||
} else {
|
||||
cur.Which = 1
|
||||
}
|
||||
}
|
||||
|
||||
// newWeaponThing rolls a weapon, sometimes cursed or blessed (things.c
|
||||
// new_thing).
|
||||
func (g *RogueGame) newWeaponThing(cur *Object) {
|
||||
g.initWeapon(cur, WeaponKind(pickOne(g, g.Items.Weapons[:NumWeaponTypes])))
|
||||
|
||||
if r := g.rnd(100); r < 10 {
|
||||
cur.Flags.Set(Cursed)
|
||||
cur.HPlus -= g.rnd(3) + 1
|
||||
} else if r < 15 {
|
||||
cur.HPlus += g.rnd(3) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// newArmorThing rolls armor, sometimes cursed or blessed (things.c
|
||||
// new_thing).
|
||||
func (g *RogueGame) newArmorThing(cur *Object) {
|
||||
cur.Kind = KindArmor
|
||||
cur.Which = pickOne(g, g.Items.Armors[:])
|
||||
|
||||
cur.ArmorClass = g.data.aClass[cur.Which]
|
||||
if r := g.rnd(100); r < 20 {
|
||||
cur.Flags.Set(Cursed)
|
||||
cur.ArmorClass += g.rnd(3) + 1
|
||||
} else if r < 28 {
|
||||
cur.ArmorClass -= g.rnd(3) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// newRingThing rolls a ring, cursing the bad ones (things.c new_thing).
|
||||
func (g *RogueGame) newRingThing(cur *Object) {
|
||||
cur.Kind = KindRing
|
||||
|
||||
cur.Which = pickOne(g, g.Items.Rings[:])
|
||||
switch cur.RingKind() {
|
||||
case RingAddStrength, RingProtection, RingDexterity, RingIncreaseDamage:
|
||||
if cur.Bonus = g.rnd(3); cur.Bonus == 0 {
|
||||
cur.Bonus = -1
|
||||
cur.Flags.Set(Cursed)
|
||||
}
|
||||
case RingAggravateMonsters, RingTeleportation:
|
||||
cur.Flags.Set(Cursed)
|
||||
}
|
||||
}
|
||||
|
||||
// pickOne picks an item out of a list of possible objects using their
|
||||
// cumulative probabilities (things.c pick_one).
|
||||
func pickOne(g *RogueGame, info []ObjInfo) int {
|
||||
@@ -423,7 +494,6 @@ const flushSentinel = "\x00"
|
||||
|
||||
func (g *RogueGame) addLine(format string, a ...any) int {
|
||||
pg := &g.invPage
|
||||
prompt := "--Press space to continue--"
|
||||
isFlush := format == flushSentinel
|
||||
|
||||
var line string
|
||||
@@ -440,69 +510,93 @@ func (g *RogueGame) addLine(format string, a ...any) int {
|
||||
}
|
||||
|
||||
if g.Options.InvType == InvSlow {
|
||||
if !isFlush && line != "" {
|
||||
if g.msg("%s", line) == Escape {
|
||||
return Escape
|
||||
}
|
||||
}
|
||||
return g.addLineSlow(line, isFlush)
|
||||
}
|
||||
|
||||
pg.lineCnt++
|
||||
} else {
|
||||
if !pg.init {
|
||||
pg.maxlen = len(prompt)
|
||||
pg.init = true
|
||||
}
|
||||
g.addLinePaged(line, isFlush)
|
||||
|
||||
if pg.lineCnt >= NumLines-1 || isFlush {
|
||||
if g.Options.InvType == InvOver && isFlush && !pg.newpage {
|
||||
// Overlay the accumulated list in a box at the top right
|
||||
// of the screen, prompt, and restore what was beneath.
|
||||
g.msg("")
|
||||
g.refresh()
|
||||
return ^Escape
|
||||
}
|
||||
|
||||
saved := NewWindow(NumLines, NumCols)
|
||||
saved.CopyFrom(g.scr.Std)
|
||||
|
||||
lx := NumCols - pg.maxlen - 2
|
||||
for y := 0; y <= pg.lineCnt; y++ {
|
||||
for x := 0; x <= pg.maxlen; x++ {
|
||||
g.scr.Std.MvAddCh(y, lx+x, g.scr.Hw.MvInch(y, x))
|
||||
}
|
||||
}
|
||||
|
||||
g.scr.Std.MvAddStr(pg.lineCnt, lx, prompt)
|
||||
g.refresh()
|
||||
g.waitFor(' ')
|
||||
g.scr.Std.CopyFrom(saved)
|
||||
g.refresh()
|
||||
} else {
|
||||
g.scr.Hw.MvAddStr(NumLines-1, 0, prompt)
|
||||
g.scr.RefreshWin(g.scr.Hw)
|
||||
g.waitFor(' ')
|
||||
g.scr.Hw.Clear()
|
||||
g.refresh()
|
||||
}
|
||||
|
||||
pg.newpage = true
|
||||
pg.lineCnt = 0
|
||||
pg.maxlen = len(prompt)
|
||||
}
|
||||
|
||||
if !isFlush && (pg.lineCnt != 0 || line != "") {
|
||||
g.scr.Hw.MvAddStr(pg.lineCnt, 0, line)
|
||||
|
||||
pg.lineCnt++
|
||||
if pg.maxlen < len(line) {
|
||||
pg.maxlen = len(line)
|
||||
}
|
||||
|
||||
pg.lastLine = line
|
||||
// addLineSlow shows one discovery line as a message (the slow-inventory
|
||||
// arm of things.c add_line).
|
||||
func (g *RogueGame) addLineSlow(line string, isFlush bool) int {
|
||||
if !isFlush && line != "" {
|
||||
if g.msg("%s", line) == Escape {
|
||||
return Escape
|
||||
}
|
||||
}
|
||||
|
||||
g.invPage.lineCnt++
|
||||
|
||||
return ^Escape
|
||||
}
|
||||
|
||||
// addLinePaged accumulates discovery lines into the paged window,
|
||||
// prompting between full pages (the windowed arm of things.c add_line).
|
||||
func (g *RogueGame) addLinePaged(line string, isFlush bool) {
|
||||
pg := &g.invPage
|
||||
prompt := "--Press space to continue--"
|
||||
|
||||
if !pg.init {
|
||||
pg.maxlen = len(prompt)
|
||||
pg.init = true
|
||||
}
|
||||
|
||||
if pg.lineCnt >= NumLines-1 || isFlush {
|
||||
if g.Options.InvType == InvOver && isFlush && !pg.newpage {
|
||||
g.addLineOverlay(prompt)
|
||||
} else {
|
||||
g.scr.Hw.MvAddStr(NumLines-1, 0, prompt)
|
||||
g.scr.RefreshWin(g.scr.Hw)
|
||||
g.waitFor(' ')
|
||||
g.scr.Hw.Clear()
|
||||
g.refresh()
|
||||
}
|
||||
|
||||
pg.newpage = true
|
||||
pg.lineCnt = 0
|
||||
pg.maxlen = len(prompt)
|
||||
}
|
||||
|
||||
if !isFlush && (pg.lineCnt != 0 || line != "") {
|
||||
g.scr.Hw.MvAddStr(pg.lineCnt, 0, line)
|
||||
|
||||
pg.lineCnt++
|
||||
if pg.maxlen < len(line) {
|
||||
pg.maxlen = len(line)
|
||||
}
|
||||
|
||||
pg.lastLine = line
|
||||
}
|
||||
}
|
||||
|
||||
// addLineOverlay draws the accumulated list in a box at the top right
|
||||
// of the screen, prompts, and restores what was beneath (things.c
|
||||
// add_line).
|
||||
func (g *RogueGame) addLineOverlay(prompt string) {
|
||||
pg := &g.invPage
|
||||
|
||||
g.msg("")
|
||||
g.refresh()
|
||||
|
||||
saved := NewWindow(NumLines, NumCols)
|
||||
saved.CopyFrom(g.scr.Std)
|
||||
|
||||
lx := NumCols - pg.maxlen - 2
|
||||
for y := 0; y <= pg.lineCnt; y++ {
|
||||
for x := 0; x <= pg.maxlen; x++ {
|
||||
g.scr.Std.MvAddCh(y, lx+x, g.scr.Hw.MvInch(y, x))
|
||||
}
|
||||
}
|
||||
|
||||
g.scr.Std.MvAddStr(pg.lineCnt, lx, prompt)
|
||||
g.refresh()
|
||||
g.waitFor(' ')
|
||||
g.scr.Std.CopyFrom(saved)
|
||||
g.refresh()
|
||||
}
|
||||
|
||||
// flushLine is add_line(NULL): force out the accumulated page.
|
||||
func (g *RogueGame) flushLine() int { return g.addLine(flushSentinel) }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user