mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
N6/C2: player-initiated duels — staked, no-death PvP
`!duel @user [stake]` (+ `!adventure duel`; accept/decline/status). A staked arena bout resolved through the auto-resolve combat engine. The engine is asymmetric — N player-seats vs one monster-shaped enemy, no seat for a second PC — so a duel builds each fighter as their real player Combatant and synthesises the opponent as an enemy stat block from their sheet. That is lopsided (player side = full kit, enemy = flat stat block), so the bout runs BOTH orientations and decides on aggregate remaining-HP fraction, cancelling the attacker-seat edge. To-hit stays faithful both ways (d20 + AttackBonus vs AC); damage folds into one enemy Attack (per-hit × swings + companion-proc expectation); DamageReduct ports over. Casters fight weapon-only in both orientations — accepted, PvP class balance is out of scope per the plan. Economics: both escrow the stake on accept (pool 2×stake); winner 70%, community pot rakes 30% (a sink); exact-HP-tie draw refunds both. Stake capped at level×€500. W/L reuses adventure_rival_records; shared 7-day pair cooldown. Terminal fate is serialised by an atomic claim (DELETE … WHERE id=? gated on RowsAffected==1, mirroring communityPotDebit): accept/decline/the expiry ticker all claim first, only the winner moves euros — no stake can be both resolved and refunded across the 24h boundary. issueDuel runs under advUserLock; accept re-checks the challenger and builds both fighters before debiting the challenged. Rides the 1-min eventTicker; combat golden byte-identical; suite green. Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
@@ -169,6 +169,7 @@ func (p *AdventurePlugin) Commands() []CommandDef {
|
||||
{Name: "rest", Description: "Rest up (`short`: quick breather, 1h cooldown — `long`: full night, 24h, needs housing or inn)", Usage: "!rest short|long", Category: "Games"},
|
||||
{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: "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"},
|
||||
@@ -337,6 +338,9 @@ func (p *AdventurePlugin) OnMessage(ctx MessageContext) error {
|
||||
if p.IsCommand(ctx.Body, "rest") {
|
||||
return p.handleDnDRestCmd(ctx, p.GetArgs(ctx.Body, "rest"))
|
||||
}
|
||||
if p.IsCommand(ctx.Body, "duel") {
|
||||
return p.handleDuelCmd(ctx, p.GetArgs(ctx.Body, "duel"))
|
||||
}
|
||||
if p.IsCommand(ctx.Body, "arm") {
|
||||
return p.handleDnDArmCmd(ctx, p.GetArgs(ctx.Body, "arm"))
|
||||
}
|
||||
@@ -513,6 +517,8 @@ func (p *AdventurePlugin) dispatchCommand(ctx MessageContext) error {
|
||||
return p.SendDM(ctx.Sender, advHelpText)
|
||||
case lower == "rivals":
|
||||
return p.handleRivalsCmd(ctx)
|
||||
case lower == "duel" || strings.HasPrefix(lower, "duel "):
|
||||
return p.handleDuelCmd(ctx, strings.TrimSpace(args[len("duel"):]))
|
||||
case lower == "babysit" || strings.HasPrefix(lower, "babysit "):
|
||||
return p.handleBabysitCmd(ctx, strings.TrimSpace(strings.TrimPrefix(lower, "babysit")))
|
||||
case lower == "blacksmith" || lower == "repair":
|
||||
@@ -562,6 +568,7 @@ const advHelpText = `**Adventure Commands**
|
||||
` + "`!adventure leaderboard`" + ` — View the leaderboard
|
||||
` + "`!adventure respond`" + ` — Respond to a mid-day event
|
||||
` + "`!adventure rivals`" + ` — View rival duel records
|
||||
` + "`!duel @user [stake]`" + ` — Challenge another adventurer to a staked, no-death bout (` + "`!duel accept`" + `/` + "`decline`" + `/` + "`status`" + `)
|
||||
` + "`!adventure babysit`" + ` — Adventurer Babysitting Service
|
||||
` + "`!adventure babysit auto`" + ` — Toggle auto-babysit (protects streaks on missed days)
|
||||
` + "`!adventure babysit focus <skill>`" + ` — Pick the skill auto-babysit trains (mining/fishing/foraging)
|
||||
|
||||
Reference in New Issue
Block a user