Files
gogobee/internal/plugin/dnd_zone_fish.go
prosolis c170adaf05 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>
2026-05-09 14:25:22 -07:00

77 lines
2.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package plugin
import (
"math/rand/v2"
"gogobee/internal/flavor"
)
// Resource & Combat Integration §6 — Fishing System Integration.
//
// Fishing reuses the harvest pipeline (HarvestFish action). This file
// adds the §6 zone gate, the existing FishingSkill rank bonus, the
// Ranger +20% rare-catch rule, and the Feywild Time Distortion hook.
// fishingZones is the §6.1 allow-list — only these zones expose !fish.
var fishingZones = map[ZoneID]bool{
ZoneForestShadows: true,
ZoneSunkenTemple: true,
ZoneUnderdark: true,
ZoneFeywildCrossing: true,
}
// isFishingZone returns true if !fish is permitted in zoneID.
func isFishingZone(zoneID ZoneID) bool { return fishingZones[zoneID] }
// fishingSkillBonus folds the legacy FishingSkill (010) into a small
// roll bonus so existing rank progression still pays off inside the
// expedition pipeline. +1 per 5 levels, capped at +2.
func fishingSkillBonus(fishingSkill int) int {
if fishingSkill <= 0 {
return 0
}
bonus := fishingSkill / 5
if bonus > 2 {
bonus = 2
}
return bonus
}
// rangerRareCatchUpgrade implements §6.2 — Ranger +20% rare catch rate.
// When the harvest outcome is Common or Standard for a fishing attempt
// by a Ranger, 20% chance to upgrade to Rich. Returns the (possibly
// upgraded) outcome.
func rangerRareCatchUpgrade(class DnDClass, action HarvestAction, outcome HarvestOutcome, rng *rand.Rand) HarvestOutcome {
if class != ClassRanger || action != HarvestFish {
return outcome
}
if outcome != OutcomeCommon && outcome != OutcomeStandard {
return outcome
}
if rngFloat(rng) < 0.20 {
return OutcomeRich
}
return outcome
}
// feywildFishDistortion narrates a Time Distortion flicker on a fishing
// attempt in Feywild Crossing. 15% chance per attempt; pure flavor for
// R4b (mechanical Time Loop event lives in the temporal-events system
// and is not duplicated here).
func feywildFishDistortion(zoneID ZoneID, action HarvestAction, rng *rand.Rand) string {
if zoneID != ZoneFeywildCrossing || action != HarvestFish {
return ""
}
if rngFloat(rng) >= 0.15 {
return ""
}
line := flavor.Pick(flavor.FeywildTimeDistortionHalf)
if line == "" {
line = flavor.Pick(flavor.FeywildTimeLoop)
}
if line == "" {
return ""
}
return "_⏳ " + line + "_\n\n"
}