@@ -38,11 +38,11 @@ class Animal:
|
||||
"""
|
||||
from src.i18n import t
|
||||
# 使用格式化字符串 msgid
|
||||
base_info = t("[{name}] ({realm})", name=t(self.name), realm=str(self.realm))
|
||||
info_parts = [base_info, t(self.desc)]
|
||||
base_info = t("[{name}] ({realm})", name=self.name, realm=str(self.realm))
|
||||
info_parts = [base_info, self.desc]
|
||||
|
||||
if self.materials:
|
||||
material_names = [t(material.name) for material in self.materials]
|
||||
material_names = [material.name for material in self.materials]
|
||||
materials_str = t("comma_separator").join(material_names)
|
||||
info_parts.append(t("Drops: {materials}", materials=materials_str))
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ class DeathType(Enum):
|
||||
OLD_AGE = "old_age"
|
||||
BATTLE = "battle"
|
||||
SERIOUS_INJURY = "serious_injury"
|
||||
HIDDEN_DOMAIN = "hidden_domain"
|
||||
|
||||
@dataclass
|
||||
class DeathReason:
|
||||
@@ -20,6 +21,8 @@ class DeathReason:
|
||||
return t("Killed by {killer}", killer=killer)
|
||||
elif self.death_type == DeathType.SERIOUS_INJURY:
|
||||
return t("Died from severe injuries")
|
||||
elif self.death_type == DeathType.HIDDEN_DOMAIN:
|
||||
return t("Perished in a Hidden Domain")
|
||||
elif self.death_type == DeathType.OLD_AGE:
|
||||
return t("Died of old age")
|
||||
return t(self.death_type.value)
|
||||
|
||||
@@ -364,6 +364,30 @@ EXTRA_PLUNDER_MULTIPLIER = "extra_plunder_multiplier"
|
||||
- 大量: 2
|
||||
"""
|
||||
|
||||
# 秘境相关
|
||||
EXTRA_HIDDEN_DOMAIN_DROP_PROB = "extra_hidden_domain_drop_prob"
|
||||
"""
|
||||
额外秘境掉落概率
|
||||
类型: float
|
||||
结算: src/classes/gathering/hidden_domain.py
|
||||
说明: 增加在秘境中获得宝物的概率。
|
||||
数值参考:
|
||||
- 微量: 0.05
|
||||
- 中量: 0.1
|
||||
- 大量: 0.2
|
||||
"""
|
||||
|
||||
EXTRA_HIDDEN_DOMAIN_DANGER_PROB = "extra_hidden_domain_danger_prob"
|
||||
"""
|
||||
额外秘境危险概率
|
||||
类型: float
|
||||
结算: src/classes/gathering/hidden_domain.py
|
||||
说明: 增加(或减少,负值)在秘境中遇到危险的概率。
|
||||
数值参考:
|
||||
- 降低危险: -0.1 (降低10%危险率)
|
||||
- 增加危险: 0.1
|
||||
"""
|
||||
|
||||
# --- 特殊权限 ---
|
||||
LEGAL_ACTIONS = "legal_actions"
|
||||
"""
|
||||
@@ -479,6 +503,10 @@ ALL_EFFECTS = [
|
||||
"shop_buy_price_reduction", # float - 商铺购买价格倍率减免
|
||||
"extra_plunder_multiplier", # float - 额外搜刮收益倍率
|
||||
|
||||
# 秘境相关
|
||||
"extra_hidden_domain_drop_prob", # float - 额外秘境掉落概率
|
||||
"extra_hidden_domain_danger_prob", # float - 额外秘境危险概率
|
||||
|
||||
# 特殊权限
|
||||
"legal_actions", # list[str] - 合法动作列表
|
||||
]
|
||||
|
||||
@@ -37,6 +37,8 @@ def get_effect_desc(effect_key: str) -> str:
|
||||
"cultivate_duration_reduction": "effect_cultivate_duration_reduction",
|
||||
"extra_cast_success_rate": "effect_extra_cast_success_rate",
|
||||
"extra_refine_success_rate": "effect_extra_refine_success_rate",
|
||||
"extra_hidden_domain_drop_prob": "effect_extra_hidden_domain_drop_prob",
|
||||
"extra_hidden_domain_danger_prob": "effect_extra_hidden_domain_danger_prob",
|
||||
}
|
||||
|
||||
msgid = msgid_map.get(effect_key, effect_key)
|
||||
@@ -189,7 +191,7 @@ def format_effects_to_text(effects: dict[str, Any] | list[dict[str, Any]]) -> st
|
||||
# 如果有条件,添加条件描述
|
||||
if effects.get("when"):
|
||||
cond = translate_condition(str(effects["when"]))
|
||||
return t("[{condition}] {effects}", condition=cond, effects=text)
|
||||
return f"[{cond}] {text}"
|
||||
|
||||
return text
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from .gathering import Gathering, GatheringManager
|
||||
from .auction import Auction
|
||||
from .hidden_domain import HiddenDomain
|
||||
|
||||
328
src/classes/gathering/hidden_domain.py
Normal file
328
src/classes/gathering/hidden_domain.py
Normal file
@@ -0,0 +1,328 @@
|
||||
from typing import List, Dict, Optional, Any, TYPE_CHECKING
|
||||
import random
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.classes.gathering.gathering import Gathering, register_gathering
|
||||
from src.classes.event import Event
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.world import World
|
||||
from src.classes.avatar import Avatar
|
||||
|
||||
from src.classes.item import Item
|
||||
from src.utils.df import game_configs, get_str, get_float, get_int
|
||||
from src.classes.cultivation import Realm, REALM_ORDER, REALM_RANK
|
||||
from src.classes.death_reason import DeathReason, DeathType
|
||||
from src.classes.death import handle_death
|
||||
from src.classes.weapon import get_random_weapon_by_realm
|
||||
from src.classes.auxiliary import get_random_auxiliary_by_realm
|
||||
from src.classes.technique import get_random_technique_for_avatar
|
||||
from src.i18n import t
|
||||
from src.run.log import get_logger
|
||||
|
||||
logger = get_logger().logger
|
||||
|
||||
@dataclass
|
||||
class DomainConfig:
|
||||
id: str
|
||||
name: str
|
||||
desc: str
|
||||
max_realm: Realm
|
||||
danger_prob: float
|
||||
hp_loss_percent: float
|
||||
drop_prob: float
|
||||
cd_years: int
|
||||
open_prob: float
|
||||
|
||||
@register_gathering
|
||||
class HiddenDomain(Gathering):
|
||||
"""
|
||||
秘境系统 (Hidden Domain)
|
||||
定期开启,符合境界条件的修士可进入探索,面临凶险或获得机缘。
|
||||
"""
|
||||
|
||||
# 记录每个秘境上次开启的年份 {domain_id: last_open_year}
|
||||
_domain_states: Dict[str, int] = {}
|
||||
|
||||
# 临时存储本轮开启的秘境
|
||||
_active_domains: List[DomainConfig] = []
|
||||
|
||||
# LLM Prompt ID
|
||||
STORY_PROMPT_ID = "hidden_domain_story_prompt"
|
||||
|
||||
@classmethod
|
||||
def get_story_prompt(cls) -> str:
|
||||
return t(cls.STORY_PROMPT_ID)
|
||||
|
||||
def _load_configs(self) -> List[DomainConfig]:
|
||||
"""从配置表加载秘境配置"""
|
||||
configs = []
|
||||
df = game_configs.get("hidden_domain")
|
||||
if df is None:
|
||||
return []
|
||||
|
||||
for row in df:
|
||||
try:
|
||||
# 必须字段
|
||||
conf = DomainConfig(
|
||||
id=get_str(row, "id"),
|
||||
name=get_str(row, "name"),
|
||||
desc=get_str(row, "desc"),
|
||||
max_realm=Realm.from_str(get_str(row, "max_realm")),
|
||||
danger_prob=get_float(row, "danger_prob"),
|
||||
hp_loss_percent=get_float(row, "hp_loss_percent"),
|
||||
drop_prob=get_float(row, "drop_prob"),
|
||||
cd_years=get_int(row, "cd_years"),
|
||||
open_prob=get_float(row, "open_prob"),
|
||||
)
|
||||
configs.append(conf)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load hidden domain config: {e}")
|
||||
continue
|
||||
return configs
|
||||
|
||||
def is_start(self, world: "World") -> bool:
|
||||
"""
|
||||
判断是否有秘境开启
|
||||
"""
|
||||
self._active_domains = []
|
||||
current_year = world.month_stamp.get_year()
|
||||
configs = self._load_configs()
|
||||
|
||||
for conf in configs:
|
||||
last_open = self._domain_states.get(conf.id, -999)
|
||||
|
||||
# 只有 CD 转好才进行概率判定
|
||||
if current_year - last_open >= conf.cd_years:
|
||||
if random.random() < conf.open_prob:
|
||||
self._active_domains.append(conf)
|
||||
# 立即更新状态,防止同一step多次调用导致状态不一致(虽然后续execute才算正式执行)
|
||||
# 但GatheringManager是 is_start -> execute 顺序执行,所以这里更新没问题
|
||||
# 如果需要execute失败回滚,可以把更新移到execute里。这里简单起见放在这里。
|
||||
self._domain_states[conf.id] = current_year
|
||||
|
||||
return len(self._active_domains) > 0
|
||||
|
||||
def get_related_avatars(self, world: "World") -> List[int]:
|
||||
"""
|
||||
获取所有可能参与的角色(即所有存活角色,具体筛选在 execute 中按秘境条件进行)
|
||||
"""
|
||||
return [av.id for av in world.avatar_manager.get_living_avatars()]
|
||||
|
||||
def get_info(self, world: "World") -> str:
|
||||
details = []
|
||||
for conf in self._active_domains:
|
||||
detail = t("Hidden Domain {name} opened! Entry restricted to {realm} and below.",
|
||||
name=conf.name,
|
||||
realm=str(conf.max_realm))
|
||||
details.append(detail)
|
||||
return t("Hidden Domains opened: {names}", names="\n".join(details))
|
||||
|
||||
def _get_next_realm(self, realm: Realm) -> Optional[Realm]:
|
||||
"""获取下一个大境界"""
|
||||
current_idx = REALM_RANK.get(realm)
|
||||
if current_idx is not None and current_idx + 1 < len(REALM_ORDER):
|
||||
return REALM_ORDER[current_idx + 1]
|
||||
return None
|
||||
|
||||
def _generate_loot(self, avatar: "Avatar", next_realm: Realm) -> Optional[Item]:
|
||||
"""生成掉落物:优先给予高一阶的物品"""
|
||||
# 掉落类型权重:兵器(40%), 防具(40%), 功法(20%)
|
||||
roll = random.random()
|
||||
|
||||
loot = None
|
||||
if roll < 0.4:
|
||||
# 兵器
|
||||
loot = get_random_weapon_by_realm(next_realm)
|
||||
elif roll < 0.8:
|
||||
# 防具
|
||||
loot = get_random_auxiliary_by_realm(next_realm)
|
||||
else:
|
||||
# 功法:尝试获取更高级的功法
|
||||
# get_random_technique_for_avatar 根据灵根匹配,但这里我们希望给一点“机缘”
|
||||
# 复用该函数,但可能获取到同阶的。为了体现“机缘”,我们允许多试几次取最好的,或者直接给
|
||||
# 这里简单调用现有接口
|
||||
loot = get_random_technique_for_avatar(avatar)
|
||||
|
||||
return loot
|
||||
|
||||
async def execute(self, world: "World") -> List[Event]:
|
||||
events = []
|
||||
|
||||
for domain in self._active_domains:
|
||||
domain_events = await self._process_single_domain(world, domain)
|
||||
events.extend(domain_events)
|
||||
|
||||
return events
|
||||
|
||||
async def _process_single_domain(self, world: "World", domain: DomainConfig) -> List[Event]:
|
||||
"""处理单个秘境的逻辑"""
|
||||
events = []
|
||||
month_stamp = world.month_stamp
|
||||
|
||||
# 1. 筛选进入秘境的角色
|
||||
entrants: List["Avatar"] = []
|
||||
for av in world.avatar_manager.get_living_avatars():
|
||||
# 境界判定:realm <= max (取消最低限制,允许越阶挑战)
|
||||
if av.cultivation_progress.realm <= domain.max_realm:
|
||||
entrants.append(av)
|
||||
|
||||
# 添加开启事件
|
||||
entrants_names = [av.name for av in entrants]
|
||||
if entrants_names:
|
||||
entrants_str = ", ".join(entrants_names)
|
||||
open_event_content = t("Hidden Domain {name} opened! Entry restricted to {realm} and below. Entrants: {entrants}",
|
||||
name=domain.name,
|
||||
realm=str(domain.max_realm),
|
||||
entrants=entrants_str)
|
||||
else:
|
||||
open_event_content = t("Hidden Domain {name} opened! Entry restricted to {realm} and below. No one entered.",
|
||||
name=domain.name,
|
||||
realm=str(domain.max_realm))
|
||||
events.append(Event(month_stamp, open_event_content))
|
||||
|
||||
if not entrants:
|
||||
return events
|
||||
|
||||
# 记录本次秘境的事件文本和相关角色
|
||||
event_texts: List[str] = [open_event_content]
|
||||
related_avatars_set: set["Avatar"] = set()
|
||||
|
||||
# 2. 遍历角色执行逻辑
|
||||
for av in entrants:
|
||||
# --- 效果结算 ---
|
||||
extra_drop = float(av.effects.get("extra_hidden_domain_drop_prob", 0.0))
|
||||
extra_danger = float(av.effects.get("extra_hidden_domain_danger_prob", 0.0))
|
||||
|
||||
drop_prob = domain.drop_prob + extra_drop
|
||||
danger_prob = domain.danger_prob + extra_danger
|
||||
|
||||
# 确保概率合理
|
||||
danger_prob = max(0.0, danger_prob)
|
||||
|
||||
# --- 凶险判定 ---
|
||||
if random.random() < danger_prob:
|
||||
loss_percent = domain.hp_loss_percent
|
||||
damage = int(av.hp.max * loss_percent)
|
||||
av.hp.cur -= damage
|
||||
|
||||
if av.hp.cur <= 0:
|
||||
# 死亡结算
|
||||
reason = DeathReason(DeathType.HIDDEN_DOMAIN)
|
||||
handle_death(world, av, reason)
|
||||
|
||||
event_content = t("{name} perished in the hidden domain {domain}.", name=av.name, domain=domain.name)
|
||||
event = Event(
|
||||
month_stamp,
|
||||
event_content,
|
||||
related_avatars=[av.id]
|
||||
)
|
||||
events.append(event)
|
||||
|
||||
event_texts.append(event_content)
|
||||
related_avatars_set.add(av)
|
||||
continue # 死了就不能拿奖励了
|
||||
|
||||
# --- 机缘判定 ---
|
||||
if random.random() < drop_prob:
|
||||
# 获取奖励阶位(高一阶)
|
||||
target_realm = self._get_next_realm(av.cultivation_progress.realm)
|
||||
# 如果已经是最高阶,则维持当前阶位
|
||||
if not target_realm:
|
||||
target_realm = av.cultivation_progress.realm
|
||||
|
||||
loot = self._generate_loot(av, target_realm)
|
||||
|
||||
if loot:
|
||||
# 发放奖励
|
||||
from src.classes.weapon import Weapon
|
||||
from src.classes.auxiliary import Auxiliary
|
||||
from src.classes.technique import Technique
|
||||
from src.classes.prices import prices
|
||||
|
||||
loot_name = loot.name
|
||||
|
||||
if isinstance(loot, Weapon):
|
||||
old = av.weapon
|
||||
av.change_weapon(loot)
|
||||
if old: # 回收旧物
|
||||
av.magic_stone += prices.get_selling_price(old, av)
|
||||
|
||||
elif isinstance(loot, Auxiliary):
|
||||
old = av.auxiliary
|
||||
av.change_auxiliary(loot)
|
||||
if old:
|
||||
av.magic_stone += prices.get_selling_price(old, av)
|
||||
|
||||
elif isinstance(loot, Technique):
|
||||
# 只有当比当前功法好,或者还没功法时才更换?
|
||||
# 或者直接放入背包(如果有)?目前 Avatar 没有通用背包,通常直接修习
|
||||
# 简化逻辑:直接修习
|
||||
av.technique = loot
|
||||
|
||||
# 记录事件
|
||||
event_content = t("{name} found a treasure {loot} in {domain}!", name=av.name, loot=loot_name, domain=domain.name)
|
||||
event = Event(
|
||||
month_stamp,
|
||||
event_content,
|
||||
related_avatars=[av.id]
|
||||
)
|
||||
events.append(event)
|
||||
|
||||
event_texts.append(event_content)
|
||||
related_avatars_set.add(av)
|
||||
|
||||
# 3. 生成故事 (StoryTeller)
|
||||
# 只有当发生了一些值得记录的事情(死人、或者有人获得重宝)才生成故事,避免刷屏
|
||||
if event_texts:
|
||||
story_event = await self._generate_story(world, domain, event_texts, list(related_avatars_set))
|
||||
if story_event:
|
||||
events.append(story_event)
|
||||
|
||||
return events
|
||||
|
||||
async def _generate_story(
|
||||
self,
|
||||
world: "World",
|
||||
domain: DomainConfig,
|
||||
event_texts: List[str],
|
||||
related_avatars: List["Avatar"]
|
||||
) -> Optional[Event]:
|
||||
"""调用 LLM 生成秘境探索故事"""
|
||||
|
||||
if not related_avatars:
|
||||
return None
|
||||
|
||||
# 1. 场景描述
|
||||
gathering_info = t(
|
||||
"Event: Hidden Domain Opening\nName: {name}\nDescription: {desc}",
|
||||
name=domain.name, desc=domain.desc
|
||||
)
|
||||
|
||||
# 2. 事件列表
|
||||
events_str = "\n".join(event_texts)
|
||||
|
||||
# 3. 角色信息 (可选,增加故事细节)
|
||||
details_list = []
|
||||
details_list.append(t("【Related Avatars Information】"))
|
||||
for av in related_avatars:
|
||||
info = av.get_info(detailed=True)
|
||||
details_list.append(f"- {av.name}: {info}")
|
||||
details_text = "\n".join(details_list)
|
||||
|
||||
# 4. 调用 StoryTeller
|
||||
from src.classes.story_teller import StoryTeller
|
||||
story = await StoryTeller.tell_gathering_story(
|
||||
gathering_info=gathering_info,
|
||||
events_text=events_str,
|
||||
details_text=details_text,
|
||||
related_avatars=related_avatars,
|
||||
prompt=self.get_story_prompt()
|
||||
)
|
||||
|
||||
return Event(
|
||||
month_stamp=world.month_stamp,
|
||||
content=story,
|
||||
related_avatars=[av.id for av in related_avatars],
|
||||
is_major=True
|
||||
)
|
||||
@@ -37,11 +37,11 @@ class Lode:
|
||||
"""
|
||||
from src.i18n import t
|
||||
# 使用格式化字符串 msgid
|
||||
base_info = t("[{name}] ({realm})", name=t(self.name), realm=str(self.realm))
|
||||
info_parts = [base_info, t(self.desc)]
|
||||
base_info = t("[{name}] ({realm})", name=self.name, realm=str(self.realm))
|
||||
info_parts = [base_info, self.desc]
|
||||
|
||||
if self.materials:
|
||||
material_names = [t(material.name) for material in self.materials]
|
||||
material_names = [material.name for material in self.materials]
|
||||
materials_str = t("comma_separator").join(material_names)
|
||||
info_parts.append(t("Drops: {materials}", materials=materials_str))
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ class Material(Item):
|
||||
|
||||
def get_info(self) -> str:
|
||||
from src.i18n import t
|
||||
return t("{name} ({realm})", name=t(self.name), realm=str(self.realm))
|
||||
return t("{name} ({realm})", name=self.name, realm=str(self.realm))
|
||||
|
||||
def get_detailed_info(self) -> str:
|
||||
from src.i18n import t
|
||||
return t("{name}: {desc} ({realm})", name=t(self.name), desc=t(self.desc), realm=str(self.realm))
|
||||
return t("{name}: {desc} ({realm})", name=self.name, desc=self.desc, realm=str(self.realm))
|
||||
|
||||
def get_structured_info(self) -> dict:
|
||||
return {
|
||||
|
||||
@@ -38,11 +38,11 @@ class Plant:
|
||||
"""
|
||||
from src.i18n import t
|
||||
# 使用格式化字符串 msgid
|
||||
base_info = t("[{name}] ({realm})", name=t(self.name), realm=str(self.realm))
|
||||
info_parts = [base_info, t(self.desc)]
|
||||
base_info = t("[{name}] ({realm})", name=self.name, realm=str(self.realm))
|
||||
info_parts = [base_info, self.desc]
|
||||
|
||||
if self.materials:
|
||||
material_names = [t(material.name) for material in self.materials]
|
||||
material_names = [material.name for material in self.materials]
|
||||
materials_str = t("comma_separator").join(material_names)
|
||||
info_parts.append(t("Drops: {materials}", materials=materials_str))
|
||||
|
||||
|
||||
@@ -327,8 +327,8 @@ def get_sect_info_with_rank(avatar: "Avatar", detailed: bool = False) -> str:
|
||||
# 构造详细信息,使用标准空格和括号
|
||||
detail_content = t("(Alignment: {alignment}, Style: {style}, Headquarters: {hq_name}){effect}",
|
||||
alignment=avatar.sect.alignment,
|
||||
style=t(avatar.sect.member_act_style),
|
||||
hq_name=t(hq.name),
|
||||
style=avatar.sect.member_act_style,
|
||||
hq_name=hq.name,
|
||||
effect=effect_part)
|
||||
|
||||
return f"{sect_rank_str} {detail_content}"
|
||||
|
||||
@@ -116,7 +116,7 @@ def get_rank_display_name(rank: SectRank, sect: Optional["Sect"] = None) -> str:
|
||||
if sect is not None:
|
||||
custom_name = sect.get_rank_name(rank)
|
||||
if custom_name:
|
||||
return t(custom_name)
|
||||
return custom_name
|
||||
val = DEFAULT_RANK_NAMES.get(rank, "弟子")
|
||||
return t(val)
|
||||
|
||||
|
||||
@@ -106,21 +106,21 @@ class Technique:
|
||||
if detailed:
|
||||
return self.get_detailed_info()
|
||||
from src.i18n import t
|
||||
return t("{name} ({attribute}) {grade}", name=t(self.name), attribute=str(self.attribute), grade=str(self.grade))
|
||||
return t("{name} ({attribute}) {grade}", name=self.name, attribute=str(self.attribute), grade=str(self.grade))
|
||||
|
||||
def get_detailed_info(self) -> str:
|
||||
from src.i18n import t
|
||||
effect_part = t(" Effect: {effect_desc}", effect_desc=self.effect_desc) if self.effect_desc else ""
|
||||
return t("{name} ({attribute}) {grade} {desc}{effect}",
|
||||
name=t(self.name), attribute=str(self.attribute), grade=str(self.grade),
|
||||
desc=t(self.desc), effect=effect_part)
|
||||
name=self.name, attribute=str(self.attribute), grade=str(self.grade),
|
||||
desc=self.desc, effect=effect_part)
|
||||
|
||||
def get_colored_info(self) -> str:
|
||||
"""获取带颜色标记的信息,供前端渲染使用"""
|
||||
from src.i18n import t
|
||||
r, g, b = self.grade.color_rgb
|
||||
# 使用与 get_info 相同的格式,但带有颜色标签
|
||||
info = t("{name} ({attribute}·{grade})", name=t(self.name), attribute=str(self.attribute), grade=str(self.grade))
|
||||
info = t("{name} ({attribute}·{grade})", name=self.name, attribute=str(self.attribute), grade=str(self.grade))
|
||||
return f"<color:{r},{g},{b}>{info}</color>"
|
||||
|
||||
def get_structured_info(self) -> dict:
|
||||
|
||||
@@ -44,8 +44,8 @@ class Weapon(Item):
|
||||
from src.i18n import t
|
||||
effect_part = t(" Effect: {effect_desc}", effect_desc=self.effect_desc) if self.effect_desc else ""
|
||||
return t("{name} ({type}·{realm}, {desc}){effect}",
|
||||
name=t(self.name), type=str(self.weapon_type), realm=str(self.realm),
|
||||
desc=t(self.desc), effect=effect_part)
|
||||
name=self.name, type=str(self.weapon_type), realm=str(self.realm),
|
||||
desc=self.desc, effect=effect_part)
|
||||
|
||||
def get_colored_info(self) -> str:
|
||||
"""获取带颜色标记的信息,供前端渲染使用"""
|
||||
|
||||
@@ -35,6 +35,8 @@ class World():
|
||||
gathering_manager: GatheringManager = field(default_factory=GatheringManager)
|
||||
# 世界历史
|
||||
history: "History" = field(default_factory=lambda: History())
|
||||
# 世界开始年份
|
||||
start_year: int = 0
|
||||
|
||||
def get_info(self, detailed: bool = False, avatar: Optional["Avatar"] = None) -> dict:
|
||||
"""
|
||||
@@ -106,6 +108,7 @@ class World():
|
||||
map: "Map",
|
||||
month_stamp: MonthStamp,
|
||||
events_db_path: Path,
|
||||
start_year: int = 0,
|
||||
) -> "World":
|
||||
"""
|
||||
工厂方法:创建使用 SQLite 持久化事件的 World 实例。
|
||||
@@ -114,6 +117,7 @@ class World():
|
||||
map: 地图对象。
|
||||
month_stamp: 时间戳。
|
||||
events_db_path: 事件数据库文件路径。
|
||||
start_year: 世界开始年份。
|
||||
|
||||
Returns:
|
||||
配置好的 World 实例。
|
||||
@@ -123,4 +127,5 @@ class World():
|
||||
map=map,
|
||||
month_stamp=month_stamp,
|
||||
event_manager=event_manager,
|
||||
start_year=start_year,
|
||||
)
|
||||
|
||||
Binary file not shown.
@@ -1626,6 +1626,18 @@ msgstr "Refine Success Rate"
|
||||
|
||||
|
||||
|
||||
msgid "effect_extra_hidden_domain_drop_prob"
|
||||
|
||||
msgstr "Hidden Domain Drop Rate"
|
||||
|
||||
|
||||
|
||||
msgid "effect_extra_hidden_domain_danger_prob"
|
||||
|
||||
msgstr "Hidden Domain Danger Rate"
|
||||
|
||||
|
||||
|
||||
# Action Short Names (for legal_actions)
|
||||
|
||||
msgid "action_dualcultivation_short_name"
|
||||
@@ -2086,7 +2098,7 @@ msgstr "\n【Related Avatars Information】"
|
||||
|
||||
msgid "auction_story_prompt"
|
||||
|
||||
msgstr "Select the most interesting aspect or bidding moment to describe, no need to cover everything."
|
||||
msgstr "Select the most interesting aspect or a single bid to describe, without needing to cover everything. Focus on describing the tense atmosphere during the bidding process."
|
||||
|
||||
|
||||
|
||||
@@ -3376,3 +3388,37 @@ msgstr "Two people are holding an elegant tea party."
|
||||
|
||||
msgid "action_chess_story_prompt"
|
||||
msgstr "Two people are playing chess."
|
||||
|
||||
# ============================================================================
|
||||
# Hidden Domain
|
||||
# ============================================================================
|
||||
|
||||
msgid "hidden_domain_story_prompt"
|
||||
msgstr "Describe the exploration of the hidden domain, focusing on the dangers encountered or the opportunities seized, creating a mysterious and tense atmosphere."
|
||||
|
||||
msgid "Hidden Domains opened: {names}"
|
||||
msgstr "{names}"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below."
|
||||
msgstr "Hidden Domain {name} opened! Entry restricted to {realm} and below."
|
||||
|
||||
msgid "{name} perished in the hidden domain {domain}."
|
||||
msgstr "{name} perished in the hidden domain {domain}."
|
||||
|
||||
msgid "{name} found a treasure {loot} in {domain}!"
|
||||
msgstr "{name} found a treasure {loot} in {domain}!"
|
||||
|
||||
msgid "Perished in a Hidden Domain"
|
||||
msgstr "Perished in a Hidden Domain"
|
||||
|
||||
msgid "Event: Hidden Domain Opening\nName: {name}\nDescription: {desc}"
|
||||
msgstr "Event: Hidden Domain Opening\nName: {name}\nDescription: {desc}"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below. Entrants: {entrants}"
|
||||
msgstr "Hidden Domain {name} opened! Entry restricted to {realm} and below. Entrants: {entrants}"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below. No one entered."
|
||||
msgstr "Hidden Domain {name} opened! Entry restricted to {realm} and below. No one entered."
|
||||
|
||||
msgid "【Related Avatars Information】"
|
||||
msgstr "【Related Avatars Information】"
|
||||
|
||||
Binary file not shown.
@@ -1191,6 +1191,12 @@ msgstr "铸造成功率"
|
||||
msgid "effect_extra_refine_success_rate"
|
||||
msgstr "炼丹成功率"
|
||||
|
||||
msgid "effect_extra_hidden_domain_drop_prob"
|
||||
msgstr "秘境掉落概率"
|
||||
|
||||
msgid "effect_extra_hidden_domain_danger_prob"
|
||||
msgstr "秘境凶险概率"
|
||||
|
||||
# Action 简短名称(用于 legal_actions)
|
||||
msgid "action_dualcultivation_short_name"
|
||||
msgstr "双修"
|
||||
@@ -1422,7 +1428,7 @@ msgstr "\n【相关角色信息】"
|
||||
|
||||
# LLM Prompt
|
||||
msgid "auction_story_prompt"
|
||||
msgstr "选取其中最有趣的一个侧面或一次竞价进行描写,无需面面俱到。"
|
||||
msgstr "选取其中最有趣的一个侧面或一次竞价进行描写,无需面面俱到。重点描写竞价过程中的紧张气氛。"
|
||||
|
||||
# ============================================================================
|
||||
# Fortune 系统 - 奇遇
|
||||
@@ -2222,3 +2228,38 @@ msgstr "两人正在举行一场雅致的茶会。"
|
||||
|
||||
msgid "action_chess_story_prompt"
|
||||
msgstr "两人正在对弈下棋。"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Hidden Domain (秘境)
|
||||
# ============================================================================
|
||||
|
||||
msgid "hidden_domain_story_prompt"
|
||||
msgstr "描述探索秘境的过程,侧重于遭遇的凶险或获得的机缘,营造神秘和紧张的氛围。"
|
||||
|
||||
msgid "Hidden Domains opened: {names}"
|
||||
msgstr "{names}"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below."
|
||||
msgstr "秘境【{name}】现世!限制{realm}及以下修士进入。"
|
||||
|
||||
msgid "{name} perished in the hidden domain {domain}."
|
||||
msgstr "{name} 葬身于秘境【{domain}】。"
|
||||
|
||||
msgid "{name} found a treasure {loot} in {domain}!"
|
||||
msgstr "{name} 在秘境【{domain}】中觅得宝物【{loot}】!"
|
||||
|
||||
msgid "Perished in a Hidden Domain"
|
||||
msgstr "在秘境中陨落"
|
||||
|
||||
msgid "Event: Hidden Domain Opening\nName: {name}\nDescription: {desc}"
|
||||
msgstr "事件:秘境开启\n名称:{name}\n描述:{desc}"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below. Entrants: {entrants}"
|
||||
msgstr "秘境【{name}】现世!限制{realm}及以下修士进入。进入者:{entrants}。"
|
||||
|
||||
msgid "Hidden Domain {name} opened! Entry restricted to {realm} and below. No one entered."
|
||||
msgstr "秘境【{name}】现世!限制{realm}及以下修士进入。无修士进入。"
|
||||
|
||||
msgid "【Related Avatars Information】"
|
||||
msgstr "【相关角色信息】"
|
||||
|
||||
@@ -342,10 +342,12 @@ async def init_game_async():
|
||||
game_instance["current_save_path"] = save_path
|
||||
print(f"事件数据库: {events_db_path}")
|
||||
|
||||
start_year = getattr(CONFIG.game, "start_year", 100)
|
||||
world = World.create_with_db(
|
||||
map=game_map,
|
||||
month_stamp=create_month_stamp(Year(100), Month.JANUARY),
|
||||
month_stamp=create_month_stamp(Year(start_year), Month.JANUARY),
|
||||
events_db_path=events_db_path,
|
||||
start_year=start_year,
|
||||
)
|
||||
sim = Simulator(world)
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ def load_game(save_path: Optional[Path] = None) -> Tuple["World", "Simulator", L
|
||||
# 读取世界数据
|
||||
world_data = save_data.get("world", {})
|
||||
month_stamp = MonthStamp(world_data["month_stamp"])
|
||||
start_year = world_data.get("start_year", 100)
|
||||
|
||||
# 计算事件数据库路径。
|
||||
events_db_path = get_events_db_path(save_path)
|
||||
@@ -203,6 +204,7 @@ def load_game(save_path: Optional[Path] = None) -> Tuple["World", "Simulator", L
|
||||
map=game_map,
|
||||
month_stamp=month_stamp,
|
||||
events_db_path=events_db_path,
|
||||
start_year=start_year,
|
||||
)
|
||||
|
||||
# 恢复世界历史
|
||||
|
||||
@@ -117,6 +117,7 @@ def save_game(
|
||||
|
||||
world_data = {
|
||||
"month_stamp": int(world.month_stamp),
|
||||
"start_year": world.start_year,
|
||||
"existed_sect_ids": [sect.id for sect in existed_sects],
|
||||
# 天地灵机
|
||||
"current_phenomenon_id": world.current_phenomenon.id if world.current_phenomenon else None,
|
||||
|
||||
@@ -267,6 +267,10 @@ class Simulator:
|
||||
Gathering 结算阶段:
|
||||
检查并执行注册的多人聚集事件(如拍卖会、大比等)。
|
||||
"""
|
||||
# 第一年不触发聚集事件,给予发育缓冲
|
||||
if self.world.month_stamp.get_year() <= self.world.start_year:
|
||||
return []
|
||||
|
||||
return await self.world.gathering_manager.check_and_run_all(self.world)
|
||||
|
||||
def _phase_update_celestial_phenomenon(self):
|
||||
|
||||
Reference in New Issue
Block a user