mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
mischief: you can now pay to have someone's expedition ruined
Mischief Makers M1 — the core engine, Matrix-only. `!mischief send elite @user`
debits the buyer, tells the games room a hit is out, and an hour later a monster
from the target's own level bracket walks into whatever dungeon they're in.
Survive it and they keep a cut of the money and the buyer is named; don't, and
they wake up on a cart home.
The monster comes from the target's bracket zone pool, not the arena ladder and
not the dungeon they happen to be standing in — the same selection code the M0
pricing sweep ran through, so the fee table can't drift away from the fight it
priced.
Three things that are load-bearing and don't look it:
* Survival is read off the target's HP, not PlayerWon. The engine's timeout is
a retreat, not a lethal blow — somebody who ran out the clock with HP left
held the thing off, and a bought monster that merely outlasted them hasn't
earned a maiming.
* Nobody dies for money, and that includes the party. The delivery skips
closeOutZoneWin/Loss (the fight is extrinsic to the dungeon — crediting it
would let a buyer unlock the target's kill-gated resources for them), so
nothing else floors a downed seat. Without floorMischiefRoster on BOTH
outcomes, a member the leader outlived is left alive at 0 HP, which every
`HPCurrent <= 0` gate reads as broken rather than dead.
* One live contract per target is a partial UNIQUE INDEX, not a read-then-write
check. Placement holds only the *buyer's* lock, so two buyers racing at the
same victim would both pass an in-code test. The loser is refunded.
Payouts are a percentage of the base fee, never of what the buyer actually paid —
the sign surcharge is a pure sink. Capped at 75%, so a survival purse is always
strictly less than the outlay and collusion loses to !baltransfer, which is free.
That cap is the entire anti-collusion story; no danger multiplier needed.
A crash between claiming a contract and closing it out used to be unrecoverable
in the design: the row would strand, the target could never be targeted again,
and the buyer's money was gone. The stale sweep refunds those in full — that one
is our fault, not a bet they lost.
Contract timestamps bind as Go time.Time, never CURRENT_TIMESTAMP. The driver
stores RFC3339 and SQLite's own stamp is space-separated; the two compare
lexicographically wrong.
Pete learns four mischief_* event types in a separate commit, and has to deploy
BEFORE this does — an unknown event_type is a 400, which retries and then parks
the bulletin forever.
This commit is contained in:
@@ -170,6 +170,7 @@ func (p *AdventurePlugin) Commands() []CommandDef {
|
||||
{Name: "arm", Description: "Ready an ability so it fires the moment your next fight starts", Usage: "!arm <ability>", Category: "Games"},
|
||||
{Name: "roll", Description: "Roll dice (NdN+M format, e.g. 2d6+3, d20, 4d6-1)", Usage: "!roll <dice>", Category: "Games"},
|
||||
{Name: "duel", Description: "Challenge another adventurer to a staked, no-death bout", Usage: "!duel @user [stake]", Category: "Games"},
|
||||
{Name: "mischief", Description: "Pay to send a monster after an adventurer who's out on an expedition", Usage: "!mischief send <tier> @user", Category: "Games"},
|
||||
{Name: "stats", Description: "Show your ability scores", Usage: "!stats", Category: "Games"},
|
||||
{Name: "level", Description: "Show your level and XP progress", Usage: "!level", Category: "Games"},
|
||||
{Name: "cast", Description: "Cast a spell (queues for next combat, or resolves now if out of combat)", Usage: "!cast <spell> [--upcast N] [--target @user]", Category: "Games"},
|
||||
@@ -288,6 +289,7 @@ func (p *AdventurePlugin) Init() error {
|
||||
go p.peteRosterTicker()
|
||||
go p.expeditionExtractionSweepTicker()
|
||||
go p.expeditionBoredomTicker()
|
||||
go p.mischiefTicker()
|
||||
|
||||
// Auto-cashout any arena runs left in 'awaiting' from a prior restart
|
||||
p.arenaCleanupStaleRuns()
|
||||
@@ -362,6 +364,9 @@ func (p *AdventurePlugin) OnMessage(ctx MessageContext) error {
|
||||
if p.IsCommand(ctx.Body, "duel") {
|
||||
return p.handleDuelCmd(ctx, p.GetArgs(ctx.Body, "duel"))
|
||||
}
|
||||
if p.IsCommand(ctx.Body, "mischief") {
|
||||
return p.handleMischiefCmd(ctx, p.GetArgs(ctx.Body, "mischief"))
|
||||
}
|
||||
if p.IsCommand(ctx.Body, "arm") {
|
||||
return p.handleDnDArmCmd(ctx, p.GetArgs(ctx.Body, "arm"))
|
||||
}
|
||||
@@ -543,6 +548,8 @@ func (p *AdventurePlugin) dispatchCommand(ctx MessageContext) error {
|
||||
return p.handleRivalsCmd(ctx)
|
||||
case lower == "duel" || strings.HasPrefix(lower, "duel "):
|
||||
return p.handleDuelCmd(ctx, strings.TrimSpace(args[len("duel"):]))
|
||||
case lower == "mischief" || strings.HasPrefix(lower, "mischief "):
|
||||
return p.handleMischiefCmd(ctx, strings.TrimSpace(args[len("mischief"):]))
|
||||
case lower == "babysit" || strings.HasPrefix(lower, "babysit "):
|
||||
return p.handleBabysitCmd(ctx, strings.TrimSpace(strings.TrimPrefix(lower, "babysit")))
|
||||
case lower == "blacksmith" || lower == "repair":
|
||||
|
||||
Reference in New Issue
Block a user