feat(packages): ludic.shop - prices by standing with the fountain guard, a weekly demand on its own dice, stock, buying and selling through purse and pack ports, a trade fact
Items (base price, sell fraction) and vendors (stock, buys, refuses) are the game's data handed over at boot; standing, the day, a shortage and "buys beyond its list" are the ShopWorld port; money is ShopPurse and the pack ShopPack. The week's wanted and glutted items come from an Rng seeded by the week, never the world's. The balance invariants - a shop is not a fountain, friction falls and never inverts, raw keeps its order - are the package's tests (nine). ludic.crafting: its private names carry the package's prefix too (a private name is still one global namespace with the game's - ludic.shop's `sp_seed` met the game's). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cc9cec922a
commit
12cfcb10ac
12 changed files with 663 additions and 28 deletions
63
packages/ludic.shop/trade.ludic
Normal file
63
packages/ludic.shop/trade.ludic
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# trade.ludic - standing at a counter, and the only ways anything changes hands
|
||||
export function shop_visit(v: int) -> void {
|
||||
if shop_vendor_ok(v) { shop_at = v } else { shop_at = -1 }
|
||||
}
|
||||
|
||||
export function shop_away() -> void { shop_at = -1 }
|
||||
|
||||
# the vendor at the counter, -1 none
|
||||
export function shop_vendor() -> int { return shop_at }
|
||||
|
||||
export function shop_at_counter() -> bool { return shop_at >= 0 }
|
||||
|
||||
# what `v` has on its shelves, in shelf order
|
||||
export function shop_stock(v: int) -> []int {
|
||||
let out = new []int
|
||||
if not shop_vendor_ok(v) or shop_vendor_list[v].stock == null { return out }
|
||||
let st = shop_vendor_list[v].stock
|
||||
for k in 0 .. len(st) { push(out, st[k]) }
|
||||
return out
|
||||
}
|
||||
|
||||
# what in the pack `v` would buy
|
||||
export function shop_sellable(v: int) -> []int {
|
||||
let out = new []int
|
||||
for it in 0 .. shop_items() { if ShopPack.count(it) > 0 and shop_buys(v, it) { push(out, it) } }
|
||||
return out
|
||||
}
|
||||
|
||||
function shop_trade_fact(what: int, it: int, n: int, price: int) -> void {
|
||||
let t = new ShopTrade
|
||||
t.what = what
|
||||
t.vendor = shop_at
|
||||
t.item = it
|
||||
t.n = n
|
||||
t.price = price
|
||||
q_push(shop_trades(), t)
|
||||
}
|
||||
|
||||
export function shop_can_buy(it: int) -> bool {
|
||||
return shop_item_ok(it) and ShopPurse.money() >= shop_price_buy(it) and ShopPack.room(it) > 0
|
||||
}
|
||||
|
||||
# one, paid for and in the pack; false and nothing spent when it cannot be
|
||||
export function shop_buy(it: int) -> bool {
|
||||
if not shop_can_buy(it) { return false }
|
||||
let pr = shop_price_buy(it)
|
||||
if not ShopPurse.spend(pr) { return false }
|
||||
ShopPack.add(it, 1)
|
||||
shop_trade_fact(SHOP_BOUGHT, it, 1, pr)
|
||||
return true
|
||||
}
|
||||
|
||||
# `n` to the vendor at the counter (as many as the pack has); how many went
|
||||
export function shop_sell(it: int, n: int) -> int {
|
||||
if not shop_buys(shop_at, it) { return 0 }
|
||||
let k = Math.min(n, ShopPack.count(it))
|
||||
if k <= 0 { return 0 }
|
||||
let pr = shop_price_sell(it)
|
||||
if not ShopPack.take(it, k) { return 0 }
|
||||
ShopPurse.earn(pr * k)
|
||||
shop_trade_fact(SHOP_SOLD, it, k, pr)
|
||||
return k
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue