mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Add market plugin, arena, holidays, UNO stacking fix, and multiple UX improvements
- Market plugin (#49): daily index snapshots (Yahoo Finance + Finnhub fallback), Ollama-generated summaries, !howsthemarket, !marketstatus, !marketreport commands, exchange hours with DST support, 30-min room cooldown - Adventure arena: 5-tier combat gauntlet with 20 monsters, risk-reward cashout, death lockout changed from 24h to midnight UTC across all code and flavor text - Adventure holidays: double daily actions on holidays - Adventure DM fix: 15-minute response window prevents bare numbers from triggering adventure during UNO games - Adventure scheduler: jitter between morning DMs to avoid Matrix rate limits - Adventure revive: wire up adv_revived achievement on admin revive - Column migration system for existing databases (holiday_action_taken) - Fix italic markdown rendering after newlines - Fix holdem DMs broken by space groups including DM rooms - UNO No Mercy: remove stacking escalation rule (any draw card stacks on any other) - UNO multiplayer: add turn announcements after stack absorption and turn passes, jitter between rapid-fire messages with safe mutex handling - Birthday: add €1,000 gift and 10 XP + €100 community celebration bonus - Market: guard against division by zero, nil pointer, and timezone errors - README updates: plugin count 48→49, all new commands, feature flags, architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,8 +37,9 @@ type AdventureCharacter struct {
|
||||
FishingXP int // v2
|
||||
Alive bool
|
||||
DeadUntil *time.Time
|
||||
ActionTakenToday bool
|
||||
ArenaWins int // v2
|
||||
ActionTakenToday bool
|
||||
HolidayActionTaken bool
|
||||
ArenaWins int // v2
|
||||
ArenaLosses int // v2
|
||||
InvasionScore int // v2
|
||||
Title string // v2
|
||||
@@ -213,14 +214,14 @@ func checkAdvLevelUp(char *AdventureCharacter, skill string) (bool, int) {
|
||||
func loadAdvCharacter(userID id.UserID) (*AdventureCharacter, error) {
|
||||
d := db.Get()
|
||||
c := &AdventureCharacter{}
|
||||
var alive, actionTaken int
|
||||
var alive, actionTaken, holidayTaken int
|
||||
var deadUntil sql.NullTime
|
||||
|
||||
err := d.QueryRow(`
|
||||
SELECT user_id, display_name,
|
||||
combat_level, mining_skill, foraging_skill, fishing_skill,
|
||||
combat_xp, mining_xp, foraging_xp, fishing_xp,
|
||||
alive, dead_until, action_taken_today,
|
||||
alive, dead_until, action_taken_today, holiday_action_taken,
|
||||
arena_wins, arena_losses, invasion_score, title,
|
||||
current_streak, best_streak, last_action_date, grudge_location,
|
||||
created_at, last_active_at
|
||||
@@ -228,7 +229,7 @@ func loadAdvCharacter(userID id.UserID) (*AdventureCharacter, error) {
|
||||
&c.UserID, &c.DisplayName,
|
||||
&c.CombatLevel, &c.MiningSkill, &c.ForagingSkill, &c.FishingSkill,
|
||||
&c.CombatXP, &c.MiningXP, &c.ForagingXP, &c.FishingXP,
|
||||
&alive, &deadUntil, &actionTaken,
|
||||
&alive, &deadUntil, &actionTaken, &holidayTaken,
|
||||
&c.ArenaWins, &c.ArenaLosses, &c.InvasionScore, &c.Title,
|
||||
&c.CurrentStreak, &c.BestStreak, &c.LastActionDate, &c.GrudgeLocation,
|
||||
&c.CreatedAt, &c.LastActiveAt,
|
||||
@@ -238,6 +239,7 @@ func loadAdvCharacter(userID id.UserID) (*AdventureCharacter, error) {
|
||||
}
|
||||
c.Alive = alive == 1
|
||||
c.ActionTakenToday = actionTaken == 1
|
||||
c.HolidayActionTaken = holidayTaken == 1
|
||||
if deadUntil.Valid {
|
||||
c.DeadUntil = &deadUntil.Time
|
||||
}
|
||||
@@ -283,19 +285,23 @@ func saveAdvCharacter(char *AdventureCharacter) error {
|
||||
if char.ActionTakenToday {
|
||||
actionTaken = 1
|
||||
}
|
||||
holidayTaken := 0
|
||||
if char.HolidayActionTaken {
|
||||
holidayTaken = 1
|
||||
}
|
||||
|
||||
_, err := d.Exec(`
|
||||
UPDATE adventure_characters SET
|
||||
display_name = ?, combat_level = ?, mining_skill = ?, foraging_skill = ?, fishing_skill = ?,
|
||||
combat_xp = ?, mining_xp = ?, foraging_xp = ?, fishing_xp = ?,
|
||||
alive = ?, dead_until = ?, action_taken_today = ?,
|
||||
alive = ?, dead_until = ?, action_taken_today = ?, holiday_action_taken = ?,
|
||||
arena_wins = ?, arena_losses = ?, invasion_score = ?, title = ?,
|
||||
current_streak = ?, best_streak = ?, last_action_date = ?, grudge_location = ?,
|
||||
last_active_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = ?`,
|
||||
char.DisplayName, char.CombatLevel, char.MiningSkill, char.ForagingSkill, char.FishingSkill,
|
||||
char.CombatXP, char.MiningXP, char.ForagingXP, char.FishingXP,
|
||||
alive, char.DeadUntil, actionTaken,
|
||||
alive, char.DeadUntil, actionTaken, holidayTaken,
|
||||
char.ArenaWins, char.ArenaLosses, char.InvasionScore, char.Title,
|
||||
char.CurrentStreak, char.BestStreak, char.LastActionDate, char.GrudgeLocation,
|
||||
string(char.UserID),
|
||||
@@ -400,7 +406,7 @@ func loadAllAdvCharacters() ([]AdventureCharacter, error) {
|
||||
SELECT user_id, display_name,
|
||||
combat_level, mining_skill, foraging_skill, fishing_skill,
|
||||
combat_xp, mining_xp, foraging_xp, fishing_xp,
|
||||
alive, dead_until, action_taken_today,
|
||||
alive, dead_until, action_taken_today, holiday_action_taken,
|
||||
arena_wins, arena_losses, invasion_score, title,
|
||||
current_streak, best_streak, last_action_date, grudge_location,
|
||||
created_at, last_active_at
|
||||
@@ -413,13 +419,13 @@ func loadAllAdvCharacters() ([]AdventureCharacter, error) {
|
||||
var chars []AdventureCharacter
|
||||
for rows.Next() {
|
||||
c := AdventureCharacter{}
|
||||
var alive, actionTaken int
|
||||
var alive, actionTaken, holidayTaken int
|
||||
var deadUntil sql.NullTime
|
||||
if err := rows.Scan(
|
||||
&c.UserID, &c.DisplayName,
|
||||
&c.CombatLevel, &c.MiningSkill, &c.ForagingSkill, &c.FishingSkill,
|
||||
&c.CombatXP, &c.MiningXP, &c.ForagingXP, &c.FishingXP,
|
||||
&alive, &deadUntil, &actionTaken,
|
||||
&alive, &deadUntil, &actionTaken, &holidayTaken,
|
||||
&c.ArenaWins, &c.ArenaLosses, &c.InvasionScore, &c.Title,
|
||||
&c.CurrentStreak, &c.BestStreak, &c.LastActionDate, &c.GrudgeLocation,
|
||||
&c.CreatedAt, &c.LastActiveAt,
|
||||
@@ -428,6 +434,7 @@ func loadAllAdvCharacters() ([]AdventureCharacter, error) {
|
||||
}
|
||||
c.Alive = alive == 1
|
||||
c.ActionTakenToday = actionTaken == 1
|
||||
c.HolidayActionTaken = holidayTaken == 1
|
||||
if deadUntil.Valid {
|
||||
c.DeadUntil = &deadUntil.Time
|
||||
}
|
||||
@@ -441,7 +448,7 @@ func resetAllAdvDailyActions() error {
|
||||
// Only reset actions taken before today — protects against race if a player
|
||||
// resolves their action at exactly midnight.
|
||||
today := time.Now().UTC().Format("2006-01-02")
|
||||
_, err := d.Exec(`UPDATE adventure_characters SET action_taken_today = 0 WHERE last_action_date < ? OR last_action_date IS NULL`, today)
|
||||
_, err := d.Exec(`UPDATE adventure_characters SET action_taken_today = 0, holiday_action_taken = 0 WHERE last_action_date < ? OR last_action_date IS NULL`, today)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user