mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Adv 2.0 D&D Phase 10 SUB2a-ii: Battle Master + Assassin L5/L7
Battle Master:
- New "superiority" resource pool (4/long-rest at L5), provisioned via
initSubclassResources at !subclass selection.
- Three armed maneuvers fueled by superiority dice:
* Precision Attack — +d8 (≈+4) to first attack roll
* Tripping Attack — enemy skips first attack (reuses Phase 9
SpellEnemySkipFirst)
* Rally — +(d8 + CHA) HealItem at <50% HP
- L7 Know Your Enemy proxied as +1 AttackBonus passive.
Assassin:
- L5 Assassinate: AssassinateAdvantage (re-roll first miss, take better
of two d20s) + AssassinateBonusDmg = level (5 at L5) stacked on top
of the Rogue's existing Sneak Attack auto-crit.
- L7 Impostor bumps the bonus damage by +3.
- L5 Infiltration Expertise → +5 Deception; L7 Impostor → +10.
Engine:
- CombatModifiers gains FirstAttackBonus, AssassinateAdvantage,
AssassinateBonusDmg.
- resolvePlayerAttack consumes each on the first attack only via new
combatState one-shot flags.
Tests added: 14 covering passive gating (BM L7+, Assassin L5/L7),
maneuver Apply flags, superiority pool init for BM only, !arm gating
across class/subclass for precision_attack, Deception bonus tiers,
statistical lift from FirstAttackBonus and AssassinateBonusDmg in
SimulateCombat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -152,6 +152,35 @@ func initResources(userID id.UserID, class DnDClass) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// subclassResourceMax — resources granted by a subclass on top of the class
|
||||
// pool. Phase 10 SUB2a-ii: Battle Master gets 4 superiority dice (5e
|
||||
// long-rest refresh in our model; spec says short-rest, but we keep parity
|
||||
// with the existing class pools' refresh cadence). Returns ("", 0) if the
|
||||
// subclass has no extra pool.
|
||||
func subclassResourceMax(sub DnDSubclass) (string, int) {
|
||||
switch sub {
|
||||
case SubclassBattleMaster:
|
||||
return "superiority", 4
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
|
||||
// initSubclassResources adds the subclass-specific resource pool. Called
|
||||
// from applySubclassChoice; idempotent. If a player switches subclasses,
|
||||
// the prior pool's row is left in place — refreshAllResources still touches
|
||||
// it but no ability references it once the subclass changes, so it's inert.
|
||||
func initSubclassResources(userID id.UserID, sub DnDSubclass) error {
|
||||
resType, max := subclassResourceMax(sub)
|
||||
if resType == "" {
|
||||
return nil
|
||||
}
|
||||
_, err := db.Get().Exec(`
|
||||
INSERT OR IGNORE INTO dnd_resources (user_id, resource_type, current_value, max_value)
|
||||
VALUES (?, ?, ?, ?)`,
|
||||
string(userID), resType, max, max)
|
||||
return err
|
||||
}
|
||||
|
||||
// getResource returns (current, max). Returns (0, 0, true) if no row exists.
|
||||
func getResource(userID id.UserID, resType string) (int, int, error) {
|
||||
var cur, max int
|
||||
|
||||
Reference in New Issue
Block a user