Files
Erupe/server/channelserver/repo_guild_hunt.go
Houmgaor 302453ce8e refactor(channelserver): split repo_guild.go into domain-focused files
The 1004-line monolith covered 11 game subsystems across 62 methods.
Split into 7 files by domain (RP, posts, alliances, adventures, hunts,
cooking) while keeping core CRUD/membership/scouts in the original.
Same package, receiver, and interface — no behavior changes.
2026-02-22 16:42:03 +01:00

136 lines
4.9 KiB
Go

package channelserver
import (
"database/sql"
"errors"
"time"
)
// GuildKill represents a kill log entry for guild hunt data.
type GuildKill struct {
ID uint32 `db:"id"`
Monster uint32 `db:"monster"`
}
// GetPendingHunt returns the pending (unacquired) hunt for a character, or nil if none.
func (r *GuildRepository) GetPendingHunt(charID uint32) (*TreasureHunt, error) {
hunt := &TreasureHunt{}
err := r.db.QueryRowx(
`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`,
charID).StructScan(hunt)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
if err != nil {
return nil, err
}
return hunt, nil
}
// ListGuildHunts returns acquired level-2 hunts for a guild, with hunter counts and claim status.
func (r *GuildRepository) ListGuildHunts(guildID, charID uint32) ([]*TreasureHunt, error) {
rows, err := r.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data,
(SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters,
CASE
WHEN ghc.character_id IS NOT NULL THEN true
ELSE false
END AS claimed
FROM guild_hunts gh
LEFT JOIN guild_hunts_claimed ghc ON gh.id = ghc.hunt_id AND ghc.character_id = $1
WHERE gh.guild_id=$2 AND gh.level=2 AND gh.acquired=TRUE
`, charID, guildID)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
var hunts []*TreasureHunt
for rows.Next() {
hunt := &TreasureHunt{}
if err := rows.StructScan(hunt); err != nil {
continue
}
hunts = append(hunts, hunt)
}
return hunts, nil
}
// CreateHunt inserts a new guild treasure hunt.
func (r *GuildRepository) CreateHunt(guildID, hostID, destination, level uint32, huntData []byte, catsUsed string) error {
_, err := r.db.Exec(
`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6)`,
guildID, hostID, destination, level, huntData, catsUsed)
return err
}
// AcquireHunt marks a treasure hunt as acquired.
func (r *GuildRepository) AcquireHunt(huntID uint32) error {
_, err := r.db.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, huntID)
return err
}
// RegisterHuntReport sets a character's active treasure hunt.
func (r *GuildRepository) RegisterHuntReport(huntID, charID uint32) error {
_, err := r.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, huntID, charID)
return err
}
// CollectHunt marks a hunt as collected and clears all characters' treasure_hunt references.
func (r *GuildRepository) CollectHunt(huntID uint32) error {
if _, err := r.db.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, huntID); err != nil {
return err
}
_, err := r.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, huntID)
return err
}
// ClaimHuntReward records that a character has claimed a treasure hunt reward.
func (r *GuildRepository) ClaimHuntReward(huntID, charID uint32) error {
_, err := r.db.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, huntID, charID)
return err
}
// ListGuildKills returns kill log entries for guild members since the character's last box claim.
func (r *GuildRepository) ListGuildKills(guildID, charID uint32) ([]*GuildKill, error) {
rows, err := r.db.Queryx(`SELECT kl.id, kl.monster FROM kill_logs kl
INNER JOIN guild_characters gc ON kl.character_id = gc.character_id
WHERE gc.guild_id=$1
AND kl.timestamp >= (SELECT box_claimed FROM guild_characters WHERE character_id=$2)
`, guildID, charID)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
var kills []*GuildKill
for rows.Next() {
kill := &GuildKill{}
if err := rows.StructScan(kill); err != nil {
continue
}
kills = append(kills, kill)
}
return kills, nil
}
// CountGuildKills returns the count of kill log entries for guild members since the character's last box claim.
func (r *GuildRepository) CountGuildKills(guildID, charID uint32) (int, error) {
var count int
err := r.db.QueryRow(`SELECT COUNT(*) FROM kill_logs kl
INNER JOIN guild_characters gc ON kl.character_id = gc.character_id
WHERE gc.guild_id=$1
AND kl.timestamp >= (SELECT box_claimed FROM guild_characters WHERE character_id=$2)
`, guildID, charID).Scan(&count)
return count, err
}
// ClearTreasureHunt clears the treasure_hunt field for a character on logout.
func (r *GuildRepository) ClearTreasureHunt(charID uint32) error {
_, err := r.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, charID)
return err
}
// InsertKillLog records a monster kill log entry for a character.
func (r *GuildRepository) InsertKillLog(charID uint32, monster int, quantity uint8, timestamp time.Time) error {
_, err := r.db.Exec(`INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, $2, $3, $4)`, charID, monster, quantity, timestamp)
return err
}