aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_bank.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-15 13:56:10 -0400
committerhistoria <[not public]>2026-09-15 13:56:48 -0400
commitb3568f03e4103dd46db2ccc680fbab0a6454d604 (patch)
treedb3f6bb4f3d7c1deefbcd50de0f5022756d6b9ae /internal/game/cmd_bank.go
parentf4ea40c4b3589104dfaced3481ba19c5735889ae (diff)
downloadthehouseoficarus-b3568f03e4103dd46db2ccc680fbab0a6454d604.tar.gz
fix: telnet buffer reuse, bank stack handling,
transport casino gate, and admin cache invalidation
Diffstat (limited to 'internal/game/cmd_bank.go')
-rw-r--r--internal/game/cmd_bank.go76
1 files changed, 47 insertions, 29 deletions
diff --git a/internal/game/cmd_bank.go b/internal/game/cmd_bank.go
index 3c4cac3..86057cc 100644
--- a/internal/game/cmd_bank.go
+++ b/internal/game/cmd_bank.go
@@ -78,6 +78,7 @@ func (g *Game) showBankBrowse(sess *net.Session) {
type bankEntry struct {
slot int
+ pos int
name string
id string
qty int
@@ -94,7 +95,10 @@ func (g *Game) showBankBrowse(sess *net.Session) {
if def != nil {
itemName = def.Name
}
- entries = append(entries, bankEntry{slot: i, name: itemName, id: slot.ItemID, qty: slot.Quantity})
+ // pos is the 1-based display position; it is also the number the
+ // withdraw command accepts, so gaps in bank slot numbering can no
+ // longer make a displayed number select the wrong item.
+ entries = append(entries, bankEntry{slot: i, pos: len(entries) + 1, name: itemName, id: slot.ItemID, qty: slot.Quantity})
}
if len(entries) == 0 && p.BankedCredits <= 0 {
@@ -109,7 +113,7 @@ func (g *Game) showBankBrowse(sess *net.Session) {
for _, e := range entries {
def, _ := g.ItemStore.Load(e.id)
t.Rows = append(t.Rows, []string{
- fmt.Sprintf("%d", e.slot+1),
+ fmt.Sprintf("%d", e.pos),
g.itemColorize(sess, def, e.name),
fmt.Sprintf("%d", e.qty),
})
@@ -153,9 +157,10 @@ func (g *Game) doBankDeposit(sess *net.Session, input string) {
bankIdx := p.NextBankSlot()
p.SetBankSlot(bankIdx, &player.InventorySlot{
- ItemID: itemID,
- Quantity: qty,
- Quality: slot.Quality,
+ ItemID: itemID,
+ Quantity: qty,
+ Quality: slot.Quality,
+ MaxQuality: slot.MaxQuality,
})
g.AccountStore.SaveCharacter(p)
@@ -183,9 +188,10 @@ func (g *Game) doBankDepositAll(sess *net.Session) {
}
bankIdx := p.NextBankSlot()
p.SetBankSlot(bankIdx, &player.InventorySlot{
- ItemID: slot.ItemID,
- Quantity: slot.Quantity,
- Quality: slot.Quality,
+ ItemID: slot.ItemID,
+ Quantity: slot.Quantity,
+ Quality: slot.Quality,
+ MaxQuality: slot.MaxQuality,
})
p.SetInvSlot(i, nil)
deposited++
@@ -204,19 +210,18 @@ func (g *Game) doBankDepositAll(sess *net.Session) {
func (g *Game) doBankWithdraw(sess *net.Session, input string) {
p := sess.Player
- freeSlot := p.FirstFreeSlot()
- if freeSlot == -1 {
- sess.WriteLine("Your inventory is full.")
- return
- }
+ // No upfront capacity check here: withdrawBankItem merges stackable items
+ // into existing stacks first and only needs a free slot otherwise.
- var entries []struct {
+ type entryRef struct {
slot int
+ pos int
name string
id string
qty int
}
+ var entries []entryRef
for _, i := range p.BankSlots() {
bs := p.BankSlot(i)
if bs == nil {
@@ -227,12 +232,7 @@ func (g *Game) doBankWithdraw(sess *net.Session, input string) {
if def != nil {
itemName = def.Name
}
- entries = append(entries, struct {
- slot int
- name string
- id string
- qty int
- }{slot: i, name: itemName, id: bs.ItemID, qty: bs.Quantity})
+ entries = append(entries, entryRef{slot: i, pos: len(entries) + 1, name: itemName, id: bs.ItemID, qty: bs.Quantity})
}
idx, err := parseChoiceIndex(input)
@@ -242,12 +242,7 @@ func (g *Game) doBankWithdraw(sess *net.Session, input string) {
return
}
- var matches []struct {
- slot int
- name string
- id string
- qty int
- }
+ var matches []entryRef
lower := strings.ToLower(input)
for _, e := range entries {
if behavior.WordPrefixMatch(e.name, lower) {
@@ -293,6 +288,30 @@ func (g *Game) withdrawBankItem(sess *net.Session, p *player.Player, bankSlotIdx
return
}
+ def, _ := g.ItemStore.Load(bs.ItemID)
+
+ // Stackable withdrawals merge into a compatible stack first; a free slot
+ // is only required when the player holds none of the item. Compatibility
+ // means the exact same item instance: merging two different-quality
+ // stacks would silently destroy the withdrawn stack's properties.
+ if def != nil && def.Stackable {
+ for i := 0; i < 28; i++ {
+ if slot := p.InvSlot(i); slot != nil && slot.ItemID == bs.ItemID &&
+ slot.Quality == bs.Quality && slot.MaxQuality == bs.MaxQuality {
+ slot.Quantity += bs.Quantity
+ p.SetBankSlot(bankSlotIdx, nil)
+ g.AccountStore.SaveCharacter(p)
+ displayName := bs.ItemID
+ if def != nil {
+ displayName = def.Name
+ }
+ itemColor := g.itemColorize(sess, def, displayName)
+ sess.WriteLine(fmt.Sprintf("You withdraw %d %ss.", bs.Quantity, itemColor))
+ return
+ }
+ }
+ }
+
freeSlot := p.FirstFreeSlot()
if freeSlot == -1 {
sess.WriteLine("Your inventory is full.")
@@ -301,12 +320,11 @@ func (g *Game) withdrawBankItem(sess *net.Session, p *player.Player, bankSlotIdx
itemID := bs.ItemID
qty := bs.Quantity
- def, _ := g.ItemStore.Load(itemID)
if def != nil && def.Stackable {
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: qty})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: qty, Quality: bs.Quality, MaxQuality: bs.MaxQuality})
} else {
- p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: 1})
+ p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: 1, Quality: bs.Quality, MaxQuality: bs.MaxQuality})
if qty > 1 {
bs.Quantity--
g.AccountStore.SaveCharacter(p)