mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Adventure crafting: grant foraging XP per attempt, track lifetime crafts
Crafting was a downstream consumer of Foraging skill but never gave any XP back, so the feedback loop was one-directional — only gathering contributed to the level that gates better recipes. Now: each auto-craft attempt grants Foraging XP (success > failure). Per-tier values: tier 1: +12 / +3 tier 2: +25 / +5 tier 3: +40 / +8 tier 4: +60 / +12 tier 5: +90 / +18 Successful T1 ≈ 30% of a Foraging Success haul; T5 ≈ 40%. Failures get a small consolation grant — the attempt still produced practical knowledge (and lost ingredients). Schema: adventure_characters.crafts_succeeded INT for lifetime tracking. Migration entry included. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,7 @@ func runMigrations(d *sql.DB) error {
|
|||||||
`ALTER TABLE coop_dungeon_gifts ADD COLUMN expires_at DATETIME`,
|
`ALTER TABLE coop_dungeon_gifts ADD COLUMN expires_at DATETIME`,
|
||||||
`ALTER TABLE coop_dungeon_gifts ADD COLUMN applied_at DATETIME`,
|
`ALTER TABLE coop_dungeon_gifts ADD COLUMN applied_at DATETIME`,
|
||||||
`ALTER TABLE coop_dungeon_gifts ADD COLUMN stack_lead_id INTEGER`,
|
`ALTER TABLE coop_dungeon_gifts ADD COLUMN stack_lead_id INTEGER`,
|
||||||
|
`ALTER TABLE adventure_characters ADD COLUMN crafts_succeeded INTEGER NOT NULL DEFAULT 0`,
|
||||||
}
|
}
|
||||||
for _, stmt := range columnMigrations {
|
for _, stmt := range columnMigrations {
|
||||||
if _, err := d.Exec(stmt); err != nil {
|
if _, err := d.Exec(stmt); err != nil {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ type AdventureCharacter struct {
|
|||||||
PetMorningDefense bool
|
PetMorningDefense bool
|
||||||
AutoBabysit bool
|
AutoBabysit bool
|
||||||
StreakDecayed bool
|
StreakDecayed bool
|
||||||
|
CraftsSucceeded int
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdvEquipment struct {
|
type AdvEquipment struct {
|
||||||
@@ -445,7 +446,7 @@ func loadAdvCharacter(userID id.UserID) (*AdventureCharacter, error) {
|
|||||||
pet_chased_away, pet_reactivated, pet_arrived,
|
pet_chased_away, pet_reactivated, pet_arrived,
|
||||||
misty_encounter_count, misty_donated_count,
|
misty_encounter_count, misty_donated_count,
|
||||||
thom_animal_line_fired, pet_supply_shop_unlocked, pet_level10_date,
|
thom_animal_line_fired, pet_supply_shop_unlocked, pet_level10_date,
|
||||||
pet_morning_defense, auto_babysit, streak_decayed
|
pet_morning_defense, auto_babysit, streak_decayed, crafts_succeeded
|
||||||
FROM adventure_characters WHERE user_id = ?`, string(userID)).Scan(
|
FROM adventure_characters WHERE user_id = ?`, string(userID)).Scan(
|
||||||
&c.UserID, &c.DisplayName,
|
&c.UserID, &c.DisplayName,
|
||||||
&c.CombatLevel, &c.MiningSkill, &c.ForagingSkill, &c.FishingSkill,
|
&c.CombatLevel, &c.MiningSkill, &c.ForagingSkill, &c.FishingSkill,
|
||||||
@@ -470,7 +471,7 @@ func loadAdvCharacter(userID id.UserID) (*AdventureCharacter, error) {
|
|||||||
&petChasedAway, &petReactivated, &petArrived,
|
&petChasedAway, &petReactivated, &petArrived,
|
||||||
&c.MistyEncounterCount, &c.MistyDonatedCount,
|
&c.MistyEncounterCount, &c.MistyDonatedCount,
|
||||||
&thomAnimalLine, &petSupplyUnlocked, &c.PetLevel10Date,
|
&thomAnimalLine, &petSupplyUnlocked, &c.PetLevel10Date,
|
||||||
&petMorningDef, &autoBabysit, &streakDecayed,
|
&petMorningDef, &autoBabysit, &streakDecayed, &c.CraftsSucceeded,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -638,7 +639,8 @@ func saveAdvCharacter(char *AdventureCharacter) error {
|
|||||||
thom_animal_line_fired = ?, pet_supply_shop_unlocked = ?, pet_level10_date = ?,
|
thom_animal_line_fired = ?, pet_supply_shop_unlocked = ?, pet_level10_date = ?,
|
||||||
pet_morning_defense = ?,
|
pet_morning_defense = ?,
|
||||||
auto_babysit = ?,
|
auto_babysit = ?,
|
||||||
streak_decayed = ?
|
streak_decayed = ?,
|
||||||
|
crafts_succeeded = ?
|
||||||
WHERE user_id = ?`,
|
WHERE user_id = ?`,
|
||||||
char.DisplayName, char.CombatLevel, char.MiningSkill, char.ForagingSkill, char.FishingSkill,
|
char.DisplayName, char.CombatLevel, char.MiningSkill, char.ForagingSkill, char.FishingSkill,
|
||||||
char.CombatXP, char.MiningXP, char.ForagingXP, char.FishingXP,
|
char.CombatXP, char.MiningXP, char.ForagingXP, char.FishingXP,
|
||||||
@@ -664,6 +666,7 @@ func saveAdvCharacter(char *AdventureCharacter) error {
|
|||||||
petMorningDef,
|
petMorningDef,
|
||||||
autoBabysit,
|
autoBabysit,
|
||||||
streakDecayed,
|
streakDecayed,
|
||||||
|
char.CraftsSucceeded,
|
||||||
string(char.UserID),
|
string(char.UserID),
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
@@ -794,7 +797,7 @@ func loadAllAdvCharacters() ([]AdventureCharacter, error) {
|
|||||||
pet_chased_away, pet_reactivated, pet_arrived,
|
pet_chased_away, pet_reactivated, pet_arrived,
|
||||||
misty_encounter_count, misty_donated_count,
|
misty_encounter_count, misty_donated_count,
|
||||||
thom_animal_line_fired, pet_supply_shop_unlocked, pet_level10_date,
|
thom_animal_line_fired, pet_supply_shop_unlocked, pet_level10_date,
|
||||||
pet_morning_defense, auto_babysit, streak_decayed
|
pet_morning_defense, auto_babysit, streak_decayed, crafts_succeeded
|
||||||
FROM adventure_characters`)
|
FROM adventure_characters`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -834,7 +837,7 @@ func loadAllAdvCharacters() ([]AdventureCharacter, error) {
|
|||||||
&petChasedAway, &petReactivated, &petArrived,
|
&petChasedAway, &petReactivated, &petArrived,
|
||||||
&c.MistyEncounterCount, &c.MistyDonatedCount,
|
&c.MistyEncounterCount, &c.MistyDonatedCount,
|
||||||
&thomAnimalLine, &petSupplyUnlocked, &c.PetLevel10Date,
|
&thomAnimalLine, &petSupplyUnlocked, &c.PetLevel10Date,
|
||||||
&petMorningDef, &autoBabysit, &streakDecayed,
|
&petMorningDef, &autoBabysit, &streakDecayed, &c.CraftsSucceeded,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,9 +339,19 @@ type CraftResult struct {
|
|||||||
Success bool
|
Success bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// craftXPRewards: per-tier foraging XP granted on successful (and tiny on
|
||||||
|
// failed) auto-crafts. Successful T1 ≈ 30% of a Foraging Success haul, scaling
|
||||||
|
// up so T5 grants are meaningful. Failures get a token consolation grant —
|
||||||
|
// you tried.
|
||||||
|
var craftXPSuccess = map[int]int{1: 12, 2: 25, 3: 40, 4: 60, 5: 90}
|
||||||
|
var craftXPFailure = map[int]int{1: 3, 2: 5, 3: 8, 4: 12, 5: 18}
|
||||||
|
|
||||||
// autoCraftConsumables scans the player's inventory for craftable recipes and
|
// autoCraftConsumables scans the player's inventory for craftable recipes and
|
||||||
// attempts to craft the highest-tier recipe available. Consumes ingredients on
|
// attempts to craft the highest-tier recipe available. Consumes ingredients on
|
||||||
// attempt; on failure one ingredient is lost. Returns crafted items and results.
|
// attempt; on failure one ingredient is lost. Returns crafted items and results.
|
||||||
|
//
|
||||||
|
// Side effects: grants foraging XP per attempt (success > failure) and bumps
|
||||||
|
// the player's CraftsSucceeded counter on each success.
|
||||||
func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int) ([]CraftResult, []AdvItem) {
|
func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int) ([]CraftResult, []AdvItem) {
|
||||||
if foragingLevel < 10 {
|
if foragingLevel < 10 {
|
||||||
return nil, items
|
return nil, items
|
||||||
@@ -393,6 +403,13 @@ func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int)
|
|||||||
}
|
}
|
||||||
results = append(results, CraftResult{Recipe: bestRecipe, Success: true})
|
results = append(results, CraftResult{Recipe: bestRecipe, Success: true})
|
||||||
crafted++
|
crafted++
|
||||||
|
// Foraging XP + lifetime counter bump.
|
||||||
|
if char, err := loadAdvCharacter(userID); err == nil {
|
||||||
|
char.ForagingXP += craftXPSuccess[bestRecipe.Tier]
|
||||||
|
char.CraftsSucceeded++
|
||||||
|
checkAdvLevelUp(char, "foraging")
|
||||||
|
_ = saveAdvCharacter(char)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Failure: all ingredients destroyed
|
// Failure: all ingredients destroyed
|
||||||
for _, id := range ingredientIDs {
|
for _, id := range ingredientIDs {
|
||||||
@@ -400,6 +417,12 @@ func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int)
|
|||||||
}
|
}
|
||||||
remaining = removeItemsByIDs(remaining, ingredientIDs)
|
remaining = removeItemsByIDs(remaining, ingredientIDs)
|
||||||
results = append(results, CraftResult{Recipe: bestRecipe, Success: false})
|
results = append(results, CraftResult{Recipe: bestRecipe, Success: false})
|
||||||
|
// Token foraging XP for the attempt.
|
||||||
|
if char, err := loadAdvCharacter(userID); err == nil {
|
||||||
|
char.ForagingXP += craftXPFailure[bestRecipe.Tier]
|
||||||
|
checkAdvLevelUp(char, "foraging")
|
||||||
|
_ = saveAdvCharacter(char)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user