mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Adv 2.0 D&D Phase R R3-R5: Combat-link, zone loot, fishing, economy
R3 — Combat Event Integration: - dnd_expedition_combat.go: Combat Interrupt rolls (§4.2) with threat-clock and Ranger-wilderness modifiers; Patrol Encounters scaled by threat level; recordZoneKill writer with monsterKillTags. - Interrupt gate at head of handleHarvestCmd; patrol gate before resolveRoom in zoneCmdAdvance; kill writer wired into combat-win paths. R4a — Zone Loot Tables: - dnd_zone_loot.go: §5 loot drop tables for all 10 zones × 5 tiers with §8.1 sell-value bands. dropTierFromCR brackets + boss floor. - Hooks on combat-win in resolveCombatRoom, resolveBossRoom, runHarvestInterrupt, tryPatrolEncounter. R4b — Fishing Integration: - dnd_zone_fish.go: fishingZones allow-list, fishingSkillBonus, rangerRareCatchUpgrade (§6.2), feywildFishDistortion narration. - §6.1 fish entries added to resource registry for Forest, Sunken Temple, Underdark, Feywild zones. - !fish wired through handleHarvestCmd; zoneItemFlavor matrix populated for all 10 zones × all loot items. R5 — Economy Integration: - dnd_economy.go: !sell (post-expedition gate, single CHA Persuasion DC 17 → +15% bump), !craft (§8.2 4 exemplar recipes), !lore (INT/Arcana DC 15 recipe discovery). - dnd_known_recipe table for persistent recipe discovery. Flavor reuse: HarvestInterrupt, PatrolEncounter, CombatVictory, PlayerDeath, LootDrop*, FeywildTimeDistortion* — all existing pools. No new flavor file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -350,6 +350,9 @@ func (p *AdventurePlugin) handleHarvestCmd(ctx MessageContext, action HarvestAct
|
||||
if err != nil || char == nil {
|
||||
return p.SendDM(ctx.Sender, "No D&D character found. Run `!setup` first.")
|
||||
}
|
||||
if action == HarvestFish && !isFishingZone(exp.ZoneID) {
|
||||
return p.SendDM(ctx.Sender, "There's no water to fish in here. `!fish` works only in Forest of Shadows, Sunken Temple, Underdark, or Feywild Crossing.")
|
||||
}
|
||||
if exp.RunID == "" {
|
||||
return p.SendDM(ctx.Sender, "No active region run. Try `!region` to refresh, or restart the expedition.")
|
||||
}
|
||||
@@ -372,12 +375,56 @@ func (p *AdventurePlugin) handleHarvestCmd(ctx MessageContext, action HarvestAct
|
||||
}
|
||||
|
||||
res, _ := FindZoneResource(exp.ZoneID, nodes[idx].ResourceID)
|
||||
|
||||
// §4.2 Combat Interrupt roll — gates the harvest attempt.
|
||||
zone, _ := getZone(exp.ZoneID)
|
||||
interrupt, intTotal := resolveCombatInterrupt(
|
||||
exp.ThreatLevel, int(zone.Tier), char.Class, exp.ZoneID, nil)
|
||||
var interruptHeader string
|
||||
switch interrupt {
|
||||
case InterruptNoise:
|
||||
_ = applyThreatDelta(exp.ID, 2, "harvest noise (§4.2)")
|
||||
interruptHeader = fmt.Sprintf(
|
||||
"_⚠ Noise drifts through the corridors (interrupt roll %d). Threat +2._\n\n",
|
||||
intTotal)
|
||||
case InterruptStandard, InterruptElite, InterruptPatrol:
|
||||
var pre strings.Builder
|
||||
pre.WriteString(fmt.Sprintf(
|
||||
"**%s · %s** — interrupted before the roll (interrupt %d).\n\n",
|
||||
strings.ToTitle(string(action)[:1])+string(action)[1:], res.Name, intTotal))
|
||||
body, ended := p.runHarvestInterrupt(ctx.Sender, exp, run, zone, interrupt)
|
||||
pre.WriteString(body)
|
||||
if interrupt == InterruptElite && !ended {
|
||||
pre.WriteString("\n\n_The node is still here — you dropped the harvest mid-strike._")
|
||||
}
|
||||
if interrupt == InterruptPatrol && !ended {
|
||||
pre.WriteString("\n\n_The harvest is forfeit._")
|
||||
}
|
||||
// Persist node state untouched (no charge spent).
|
||||
_ = saveHarvestNodes(exp, roomIdx, nodes)
|
||||
_ = appendExpeditionLog(exp.ID, exp.CurrentDay, "interrupt",
|
||||
fmt.Sprintf("%s %s kind=%d total=%d", string(action), res.ID, int(interrupt), intTotal), "")
|
||||
return p.SendDM(ctx.Sender, pre.String())
|
||||
}
|
||||
|
||||
roll := rand.IntN(20) + 1
|
||||
mod := harvestActionAbility(char, action) + classHarvestBonus(char.Class, action)
|
||||
if action == HarvestFish {
|
||||
if adv, _ := loadAdvCharacter(ctx.Sender); adv != nil {
|
||||
mod += fishingSkillBonus(adv.FishingSkill)
|
||||
}
|
||||
}
|
||||
total := roll + mod
|
||||
outcome := resolveHarvestOutcome(total, res.DC)
|
||||
outcome = rangerRareCatchUpgrade(char.Class, action, outcome, nil)
|
||||
|
||||
var b strings.Builder
|
||||
if interruptHeader != "" {
|
||||
b.WriteString(interruptHeader)
|
||||
}
|
||||
if dist := feywildFishDistortion(exp.ZoneID, action, nil); dist != "" {
|
||||
b.WriteString(dist)
|
||||
}
|
||||
verb := strings.ToTitle(string(action)[:1]) + string(action)[1:]
|
||||
b.WriteString(fmt.Sprintf("**%s · %s** — d20=%d + %d = **%d** vs DC %d\n\n",
|
||||
verb, res.Name, roll, mod, total, res.DC))
|
||||
|
||||
Reference in New Issue
Block a user