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:
@@ -1734,6 +1734,44 @@ CREATE TABLE IF NOT EXISTS community_pot (
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Mischief Makers: a paid monster hit on a player who is out on an expedition.
|
||||
-- The buyer's euros are debited at placement (paid); fee is the payout basis the
|
||||
-- target's survival purse is a percentage of — the sign surcharge is a pure sink
|
||||
-- and never inflates the purse, which is what keeps a payout strictly below what
|
||||
-- the buyer spent (collusion loses to !baltransfer, which is free).
|
||||
--
|
||||
-- Lifecycle: open → delivering → delivered (outcome survived|downed) | fizzled.
|
||||
-- Every transition is a conditional UPDATE, so a double-fire of the ticker or a
|
||||
-- restart mid-delivery can never pay twice. No bootstrap — absent row == no
|
||||
-- contract in flight.
|
||||
CREATE TABLE IF NOT EXISTS mischief_contracts (
|
||||
contract_id TEXT PRIMARY KEY,
|
||||
buyer_id TEXT NOT NULL,
|
||||
target_id TEXT NOT NULL,
|
||||
tier TEXT NOT NULL,
|
||||
fee INTEGER NOT NULL,
|
||||
paid INTEGER NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
signed INTEGER NOT NULL DEFAULT 0,
|
||||
escalation_count INTEGER NOT NULL DEFAULT 0,
|
||||
blessing_count INTEGER NOT NULL DEFAULT 0,
|
||||
source TEXT NOT NULL DEFAULT 'matrix',
|
||||
outcome TEXT,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
window_ends_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME
|
||||
);
|
||||
-- One live contract per target, enforced by the database rather than by a
|
||||
-- read-then-write check. The placement path holds only the BUYER's lock, so two
|
||||
-- different buyers racing to hit the same person would both pass an in-code
|
||||
-- "is one already live?" test. This partial unique index is what actually stops
|
||||
-- the second insert; the loser is refunded.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_mischief_one_live_per_target
|
||||
ON mischief_contracts(target_id) WHERE status IN ('open', 'delivering');
|
||||
CREATE INDEX IF NOT EXISTS idx_mischief_target ON mischief_contracts(target_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_mischief_buyer ON mischief_contracts(buyer_id, created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_mischief_due ON mischief_contracts(status, window_ends_at);
|
||||
|
||||
-- Babysitting Service
|
||||
CREATE TABLE IF NOT EXISTS adventure_babysit_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
Reference in New Issue
Block a user