quests update

master
Denis Ranneft 1 month ago
parent 8d2cb97614
commit f5213d05c7

@ -169,9 +169,14 @@ func (h *NPCHandler) InteractNPC(w http.ResponseWriter, r *http.Request) {
switch npc.Type {
case "quest_giver":
daySeed := time.Now().UTC().Unix() / 86400
refreshHours := tuning.EffectiveQuestOfferRefreshHours()
if refreshHours <= 0 {
refreshHours = 2
}
refreshSeconds := int64(time.Duration(refreshHours) * time.Hour / time.Second)
timeBucket := time.Now().UTC().Unix() / refreshSeconds
limit := tuning.EffectiveQuestOffersPerNPC()
quests, err := h.questStore.ListOfferableQuestsForNPC(r.Context(), hero.ID, npc.ID, hero.Level, limit, daySeed)
quests, err := h.questStore.ListOfferableQuestsForNPC(r.Context(), hero.ID, npc.ID, hero.Level, limit, timeBucket)
if err != nil {
h.logger.Error("failed to list quests for npc interaction", "npc_id", npc.ID, "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{

@ -121,7 +121,7 @@ func (h *QuestHandler) ListBuildingsByTown(w http.ResponseWriter, r *http.Reques
// ListQuestsByNPC returns quests offered by an NPC.
// GET /api/v1/npcs/{npcId}/quests
// With ?telegramId= the list is filtered (no already-logged templates), level-scoped, capped, and rotated daily — same rules as npc-interact.
// With ?telegramId= the list is filtered (no already-logged templates), level-scoped, capped, and rotated on a configured cadence — same rules as npc-interact.
// Without telegramId, returns all templates for that NPC (catalog / tools).
func (h *QuestHandler) ListQuestsByNPC(w http.ResponseWriter, r *http.Request) {
npcIDStr := chi.URLParam(r, "npcId")
@ -155,9 +155,14 @@ func (h *QuestHandler) ListQuestsByNPC(w http.ResponseWriter, r *http.Request) {
})
return
}
daySeed := time.Now().UTC().Unix() / 86400
refreshHours := tuning.EffectiveQuestOfferRefreshHours()
if refreshHours <= 0 {
refreshHours = 2
}
refreshSeconds := int64(time.Duration(refreshHours) * time.Hour / time.Second)
timeBucket := time.Now().UTC().Unix() / refreshSeconds
limit := tuning.EffectiveQuestOffersPerNPC()
quests, err := h.questStore.ListOfferableQuestsForNPC(r.Context(), hero.ID, npcID, hero.Level, limit, daySeed)
quests, err := h.questStore.ListOfferableQuestsForNPC(r.Context(), hero.ID, npcID, hero.Level, limit, timeBucket)
if err != nil {
h.logger.Error("failed to list offerable quests", "npc_id", npcID, "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{

@ -261,8 +261,8 @@ func (s *QuestStore) HeroTakenQuestTemplateIDs(ctx context.Context, heroID int64
}
// ListOfferableQuestsForNPC returns level-matching NPC quests excluding templates already on the hero's log.
// limit comes from tuning (e.g. questOffersPerNPC). daySeed should be UTC day bucket (e.g. unix/86400) for daily rotation.
func (s *QuestStore) ListOfferableQuestsForNPC(ctx context.Context, heroID, npcID int64, heroLevel int, limit int, daySeed int64) ([]model.Quest, error) {
// limit comes from tuning (e.g. questOffersPerNPC). timeBucket is a stable bucket (e.g. unixSeconds/7200) for rotations.
func (s *QuestStore) ListOfferableQuestsForNPC(ctx context.Context, heroID, npcID int64, heroLevel int, limit int, timeBucket int64) ([]model.Quest, error) {
all, err := s.ListQuestsByNPCForHeroLevel(ctx, npcID, heroLevel)
if err != nil {
return nil, err
@ -275,7 +275,7 @@ func (s *QuestStore) ListOfferableQuestsForNPC(ctx context.Context, heroID, npcI
for _, id := range takenIDs {
taken[id] = struct{}{}
}
seed := heroID ^ npcID ^ daySeed
seed := npcID ^ timeBucket
return FilterCapOfferableQuests(all, taken, limit, seed), nil
}

@ -83,6 +83,8 @@ type Values struct {
NPCCostNearbyRadius float64 `json:"npcCostNearbyRadius"`
// QuestOffersPerNPC caps how many quest templates a quest_giver offers per interaction (after filtering taken quests).
QuestOffersPerNPC int `json:"questOffersPerNPC"`
// QuestOfferRefreshHours controls how often quest_giver offers rotate (hours).
QuestOfferRefreshHours int `json:"questOfferRefreshHours"`
CombatDamageScale float64 `json:"combatDamageScale"`
CombatDamageRollMin float64 `json:"combatDamageRollMin"`
@ -276,6 +278,7 @@ func DefaultValues() Values {
NPCCostPotion: 50,
NPCCostNearbyRadius: 3.0,
QuestOffersPerNPC: 2,
QuestOfferRefreshHours: 2,
CombatDamageScale: 0.35,
CombatDamageRollMin: 0.60,
CombatDamageRollMax: 1.10,
@ -412,6 +415,15 @@ func EffectiveQuestOffersPerNPC() int {
return n
}
// EffectiveQuestOfferRefreshHours returns the rotation cadence (hours) for quest_giver offers.
func EffectiveQuestOfferRefreshHours() int {
n := Get().QuestOfferRefreshHours
if n <= 0 {
return DefaultValues().QuestOfferRefreshHours
}
return n
}
func Set(v Values) {
current.Store(&v)
}

Loading…
Cancel
Save