You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
autohero/backend/internal/model/adventure_log_english_fallb...

286 lines
7.7 KiB
Go

package model
import (
"fmt"
"strings"
)
// EnglishAdventureLogFallback returns a readable English line for SQL/admin when Message is empty.
// Keep roughly aligned with frontend adventureLogFormat.ts (EN branch). Sync new event codes here.
func EnglishAdventureLogFallback(ev *AdventureLogEvent) string {
if ev == nil {
return ""
}
a := ev.Args
switch ev.Code {
case LogCombatSwing:
return englishCombatSwing(a)
case LogDefeatedEnemy:
return fmt.Sprintf("Defeated %s (+%v XP, +%v gold).", englishEnemyLogName(a), logArgFloat(a, "xp"), logArgFloat(a, "gold"))
case LogLeveledUp:
return fmt.Sprintf("Reached level %d!", logArgInt(a, "level"))
case LogEquippedNew:
return fmt.Sprintf("Equipped new %s: %s.", englishSlotName(logArgStr(a, "slot")), logArgStr(a, "itemName"))
case LogInventoryFullDropped:
return fmt.Sprintf("Inventory full — dropped %s (%s).", logArgStr(a, "itemName"), englishRarityName(logArgStr(a, "rarity")))
case LogBuffActivated:
return fmt.Sprintf("%s activated.", englishBuffName(logArgStr(a, "buffType")))
case LogHeroRevived:
return "You revived."
case LogWanderingMerchant:
return "A hooded merchant blocks your path, jingling a pouch of odd trinkets."
case LogEncounteredEnemy:
return fmt.Sprintf("You encounter %s.", englishEnemyLogName(a))
case LogDiedFighting:
return fmt.Sprintf("You died fighting %s.", englishEnemyLogName(a))
case LogAutoReviveHours:
return "Hours passed; you revived in town."
case LogAutoReviveAfterSec:
return fmt.Sprintf("Auto-revived after %ds offline.", logArgInt(a, "seconds"))
case LogPurchasedBuffRefill:
return fmt.Sprintf("Refilled charges: %s.", englishBuffName(logArgStr(a, "buffType")))
case LogPurchasedBuffRefillRub:
return fmt.Sprintf("Purchased refill for %s (%d RUB).", englishBuffName(logArgStr(a, "buffType")), logArgInt(a, "priceRub"))
case LogSubscribed:
dk := logArgStr(a, "durationKey")
price := logArgInt(a, "priceRub")
dur := dk
if dk == "subscription.week" {
dur = "one week of subscription"
}
return fmt.Sprintf("Subscribed: %s (%d RUB).", dur, price)
case LogUsedHealingPotion:
return fmt.Sprintf("Used healing potion (+%d HP).", logArgInt(a, "amount"))
case LogAchievementUnlocked:
title := logArgStr(a, "title")
rt := logArgStr(a, "rewardType")
ra := logArgInt(a, "rewardAmount")
switch rt {
case "gold":
return fmt.Sprintf("Achievement: %s (+%d gold).", title, ra)
case "potion":
return fmt.Sprintf("Achievement: %s (+%d potions).", title, ra)
default:
return fmt.Sprintf("Achievement: %s.", title)
}
case LogMetNPC:
return fmt.Sprintf("Met %s in %s.", logArgStr(a, "npcKey"), logArgStr(a, "townKey"))
case LogWanderingAlmsEquipped:
return fmt.Sprintf("Equipped from the merchant: %s.", logArgStr(a, "itemName"))
case LogWanderingAlmsDropped:
return fmt.Sprintf("Dropped %s (%s) — no room.", logArgStr(a, "itemName"), englishRarityName(logArgStr(a, "rarity")))
case LogWanderingAlmsStashed:
return fmt.Sprintf("Stashed %s in your inventory.", logArgStr(a, "itemName"))
case LogHealedFullTown:
return "Paid for a full heal."
case LogBoughtPotionTown:
return "Bought a potion in town."
case LogSoldItemsMerchant:
return fmt.Sprintf("Sold %d items to %s (+%v gold).", logArgInt(a, "count"), logArgStr(a, "npcKey"), logArgFloat(a, "gold"))
case LogNPCSkippedVisit:
return fmt.Sprintf("Skipped visiting %s.", logArgStr(a, "npcKey"))
case LogThoughtRoadside:
idx := logArgInt(a, "idx")
return fmt.Sprintf("Roadside thought (%d).", idx)
case LogPurchasedPotionFromNPC:
return fmt.Sprintf("Bought a potion from %s.", logArgStr(a, "npcKey"))
case LogPaidHealerFull:
return fmt.Sprintf("Paid %s for a full heal.", logArgStr(a, "npcKey"))
case LogQuestGiverChecked:
return fmt.Sprintf("Checked in with %s — no new quests.", logArgStr(a, "npcKey"))
case LogQuestAccepted:
return fmt.Sprintf("Accepted quest: %s.", logArgStr(a, "title"))
case LogTownNPCVisitLine:
npcType := logArgStr(a, "npcType")
line := logArgInt(a, "line")
return fmt.Sprintf("Town visit (%s): beat %d/6.", npcType, line+1)
default:
if ev.Code != "" {
return fmt.Sprintf("[%s]", ev.Code)
}
return ""
}
}
func englishCombatSwing(a map[string]any) string {
source := logArgStr(a, "source")
outcome := logArgStr(a, "outcome")
damage := logArgInt(a, "damage")
isCrit := logArgBool(a, "isCrit")
enemyName := englishEnemyLogName(a)
critSuffix := ""
if isCrit {
critSuffix = " (crit)"
}
var msg string
switch source {
case "hero":
switch outcome {
case "stun":
msg = "You are stunned and cannot attack."
case "dodge":
msg = enemyName + " dodged your attack."
default:
msg = fmt.Sprintf("You hit %s for %d damage%s.", enemyName, damage, critSuffix)
}
case "enemy":
switch outcome {
case "block":
msg = fmt.Sprintf("You block %s's attack.", enemyName)
default:
msg = fmt.Sprintf("%s hits you for %d damage%s.", enemyName, damage, critSuffix)
}
}
debuff := logArgStr(a, "debuffType")
if debuff != "" {
msg += " " + englishDebuffName(debuff) + " applied."
}
return msg
}
// englishEnemyLogName prefers arg enemyName (DB) when present; else template name from slug.
func englishEnemyLogName(a map[string]any) string {
if a == nil {
return "enemy"
}
if n := strings.TrimSpace(logArgStr(a, "enemyName")); n != "" {
return n
}
return englishEnemyDisplayName(logArgStr(a, "enemyType"))
}
func englishEnemyDisplayName(slug string) string {
slug = strings.TrimSpace(slug)
if slug == "" {
return "enemy"
}
if e, ok := EnemyBySlug(slug); ok && strings.TrimSpace(e.Name) != "" {
return e.Name
}
return slug
}
func englishDebuffName(debuffType string) string {
dt, ok := ValidDebuffType(debuffType)
if !ok {
return debuffType
}
if def, ok := DebuffDefinition(dt); ok && def.Name != "" {
return def.Name
}
return debuffType
}
func englishBuffName(raw string) string {
raw = strings.ToLower(strings.TrimSpace(raw))
bt, ok := ValidBuffType(raw)
if !ok {
return raw
}
if b, ok := BuffDefinition(bt); ok && b.Name != "" {
return b.Name
}
return raw
}
func englishSlotName(raw string) string {
raw = strings.ToLower(strings.TrimSpace(raw))
m := map[string]string{
"main_hand": "weapon",
"off_hand": "off-hand",
"head": "head",
"chest": "chest",
"legs": "legs",
"feet": "feet",
"cloak": "cloak",
"neck": "neck",
"finger": "ring",
"wrist": "wrist",
"hands": "hands",
"quiver": "quiver",
}
if s, ok := m[raw]; ok {
return s
}
return raw
}
func englishRarityName(raw string) string {
raw = strings.ToLower(strings.TrimSpace(raw))
m := map[string]string{
"common": "common",
"uncommon": "uncommon",
"rare": "rare",
"epic": "epic",
"legendary": "legendary",
}
if s, ok := m[raw]; ok {
return s
}
return raw
}
func logArgStr(a map[string]any, key string) string {
if a == nil {
return ""
}
v, ok := a[key]
if !ok || v == nil {
return ""
}
switch x := v.(type) {
case string:
return x
case fmt.Stringer:
return x.String()
default:
return fmt.Sprint(x)
}
}
func logArgFloat(a map[string]any, key string) float64 {
if a == nil {
return 0
}
v, ok := a[key]
if !ok || v == nil {
return 0
}
switch x := v.(type) {
case float64:
return x
case float32:
return float64(x)
case int:
return float64(x)
case int64:
return float64(x)
case uint64:
return float64(x)
default:
return 0
}
}
func logArgInt(a map[string]any, key string) int {
return int(logArgFloat(a, key))
}
func logArgBool(a map[string]any, key string) bool {
if a == nil {
return false
}
v, ok := a[key]
if !ok || v == nil {
return false
}
switch x := v.(type) {
case bool:
return x
case string:
return strings.EqualFold(x, "true") || x == "1"
default:
return false
}
}