add sect ranks
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import random
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
from collections import defaultdict
|
||||
import json
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.sect_ranks import SectRank
|
||||
|
||||
from src.classes.calendar import MonthStamp
|
||||
from src.classes.action import Action
|
||||
from src.classes.action_runtime import ActionStatus, ActionResult
|
||||
@@ -89,6 +92,8 @@ class Avatar:
|
||||
alignment: Alignment | None = None
|
||||
# 所属宗门(可为空,表示散修/无门无派)
|
||||
sect: Sect | None = None
|
||||
# 宗门职位(仅当有宗门时有效)
|
||||
sect_rank: "SectRank | None" = None
|
||||
# 外貌(1~10级),创建时随机生成
|
||||
appearance: Appearance = field(default_factory=get_random_appearance)
|
||||
# 装备的法宝(仅一个)
|
||||
@@ -185,9 +190,11 @@ class Avatar:
|
||||
relations_info = ";".join(relation_lines) if relation_lines else "无"
|
||||
magic_stone_info = str(self.magic_stone)
|
||||
|
||||
from src.classes.sect import get_sect_info_with_rank
|
||||
|
||||
if detailed:
|
||||
treasure_info = self.treasure.get_detailed_info() if self.treasure is not None else "无"
|
||||
sect_info = self.sect.get_detailed_info() if self.sect is not None else "散修"
|
||||
sect_info = get_sect_info_with_rank(self, detailed=True)
|
||||
alignment_info = self.alignment.get_detailed_info() if self.alignment is not None else "未知"
|
||||
region_info = region.get_detailed_info() if region is not None else "无"
|
||||
root_info = self.root.get_detailed_info()
|
||||
@@ -199,8 +206,8 @@ class Avatar:
|
||||
spirit_animal_info = self.spirit_animal.get_info() if self.spirit_animal is not None else "无"
|
||||
else:
|
||||
treasure_info = self.treasure.get_info() if self.treasure is not None else "无"
|
||||
# personas和sect一致返回detailed,因为这俩太重要了
|
||||
sect_info = self.sect.get_detailed_info() if self.sect is not None else "散修"
|
||||
# 宗门信息:非详细模式下只显示"宗门名+职位"
|
||||
sect_info = get_sect_info_with_rank(self, detailed=False)
|
||||
region_info = region.get_info() if region is not None else "无"
|
||||
alignment_info = self.alignment.get_info() if self.alignment is not None else "未知"
|
||||
root_info = self.root.get_info()
|
||||
@@ -316,7 +323,7 @@ class Avatar:
|
||||
"""
|
||||
if self.current_action is None:
|
||||
return []
|
||||
# 记录当前动作实例引用,用于检测执行过程中是否发生了“抢占/切换”
|
||||
# 记录当前动作实例引用,用于检测执行过程中是否发生了"抢占/切换"
|
||||
action_instance_before = self.current_action
|
||||
action = action_instance_before.action
|
||||
params = action_instance_before.params
|
||||
@@ -326,9 +333,14 @@ class Avatar:
|
||||
params_for_finish = filter_kwargs_for_callable(action.finish, params)
|
||||
finish_events = action.finish(**params_for_finish)
|
||||
# 仅当当前动作仍然是刚才执行的那个实例时才清空
|
||||
# 若在 step() 内部通过“抢占”机制切换了动作(如 Escape 失败立即切到 Battle),不要清空新动作
|
||||
# 若在 step() 内部通过"抢占"机制切换了动作(如 Escape 失败立即切到 Battle),不要清空新动作
|
||||
if self.current_action is action_instance_before:
|
||||
self.current_action = None
|
||||
# 动作完成后,如果有待执行计划,立即提交下一个(支持同月链式执行)
|
||||
if self.has_plans():
|
||||
start_event = self.commit_next_plan()
|
||||
if start_event is not None:
|
||||
self._pending_events.append(start_event)
|
||||
if finish_events:
|
||||
# 允许 finish 直接返回事件(极少用),统一并入 pending
|
||||
for e in finish_events:
|
||||
@@ -338,13 +350,15 @@ class Avatar:
|
||||
for e in result.events:
|
||||
self._pending_events.append(e)
|
||||
events, self._pending_events = self._pending_events, []
|
||||
# 本轮已执行过,清除“新设动作”标记
|
||||
self._new_action_set_this_step = False
|
||||
# 本轮已执行过,清除"新设动作"标记(但如果刚刚提交了新动作,commit_next_plan会重新设置为True)
|
||||
if self.current_action is None:
|
||||
# 当前无动作时才清除标记,避免清除新提交动作的标记
|
||||
self._new_action_set_this_step = False
|
||||
return events
|
||||
|
||||
def update_cultivation(self, new_level: int):
|
||||
"""
|
||||
更新修仙进度,并在境界提升时更新寿命
|
||||
更新修仙进度,并在境界提升时更新寿命和宗门职位
|
||||
"""
|
||||
old_realm = self.cultivation_progress.realm
|
||||
self.cultivation_progress.level = new_level
|
||||
@@ -353,6 +367,9 @@ class Avatar:
|
||||
# 如果境界提升了,更新寿命期望
|
||||
if self.cultivation_progress.realm != old_realm:
|
||||
self.age.update_realm(self.cultivation_progress.realm)
|
||||
# 如果有宗门,检查是否需要晋升职位
|
||||
from src.classes.sect_ranks import check_and_promote_sect_rank
|
||||
check_and_promote_sect_rank(self, old_realm, self.cultivation_progress.realm)
|
||||
|
||||
def death_by_old_age(self) -> bool:
|
||||
"""
|
||||
@@ -595,9 +612,19 @@ class Avatar:
|
||||
|
||||
def get_sect_str(self) -> str:
|
||||
"""
|
||||
获取宗门显示名:有宗门则返回宗门名,否则返回"散修"。
|
||||
获取宗门显示名:有宗门则返回"宗门名+职位",否则返回"散修"。
|
||||
例如:"合欢宗长老"、"散修"
|
||||
"""
|
||||
return self.sect.name if self.sect is not None else "散修"
|
||||
if self.sect is None:
|
||||
return "散修"
|
||||
|
||||
# 有宗门但无职位(理论上不应该出现,兜底处理)
|
||||
if self.sect_rank is None:
|
||||
return self.sect.name
|
||||
|
||||
from src.classes.sect_ranks import get_rank_display_name
|
||||
rank_name = get_rank_display_name(self.sect_rank, self.sect)
|
||||
return f"{self.sect.name}{rank_name}"
|
||||
|
||||
def set_relation(self, other: "Avatar", relation: Relation) -> None:
|
||||
"""
|
||||
|
||||
@@ -53,12 +53,12 @@ class Talk(MutualAction):
|
||||
)
|
||||
EventHelper.push_pair(accept_event, initiator=self.avatar, target=target, to_sidebar_once=True)
|
||||
|
||||
# 立即启动 Conversation
|
||||
from .conversation import Conversation
|
||||
conv = Conversation(self.avatar, self.world)
|
||||
conv.start(target_avatar=target)
|
||||
# 直接执行一次 step,启动异步调用
|
||||
conv.step(target_avatar=target)
|
||||
# 将 Conversation 加入计划队列,在Talk完成后立即执行
|
||||
self.avatar.load_decide_result_chain(
|
||||
[("Conversation", {"target_avatar": target.name})],
|
||||
self.avatar.thinking,
|
||||
self.avatar.objective
|
||||
)
|
||||
else:
|
||||
# 拒绝攀谈
|
||||
reject_event = Event(
|
||||
|
||||
@@ -44,8 +44,8 @@ class Sect:
|
||||
weight: float = 1.0
|
||||
# 影响角色或系统的效果
|
||||
effects: dict[str, object] = field(default_factory=dict)
|
||||
# 功法:在technique.csv中配置
|
||||
# TODO:宗内等级和称谓
|
||||
# 宗门自定义职位名称(可选):SectRank -> 名称
|
||||
rank_names: dict[str, str] = field(default_factory=dict)
|
||||
|
||||
def get_info(self) -> str:
|
||||
hq = self.headquarter
|
||||
@@ -55,6 +55,20 @@ class Sect:
|
||||
# 详细描述:风格、阵营、驻地
|
||||
hq = self.headquarter
|
||||
return f"{self.name}(阵营:{self.alignment},风格:{self.member_act_style},驻地:{hq.name})"
|
||||
|
||||
def get_rank_name(self, rank: "SectRank") -> str:
|
||||
"""
|
||||
获取宗门的职位名称(支持自定义)
|
||||
|
||||
Args:
|
||||
rank: 宗门职位枚举
|
||||
|
||||
Returns:
|
||||
职位名称字符串
|
||||
"""
|
||||
from src.classes.sect_ranks import SectRank, DEFAULT_RANK_NAMES
|
||||
# 优先使用自定义名称,否则使用默认名称
|
||||
return self.rank_names.get(rank.value, DEFAULT_RANK_NAMES.get(rank, "弟子"))
|
||||
def _split_names(value: object) -> list[str]:
|
||||
raw = "" if value is None or str(value) == "nan" else str(value)
|
||||
sep = CONFIG.df.ids_separator
|
||||
@@ -136,4 +150,44 @@ def _load_sects() -> tuple[dict[int, Sect], dict[str, Sect]]:
|
||||
|
||||
|
||||
# 导出:从配表加载 sect 数据
|
||||
sects_by_id, sects_by_name = _load_sects()
|
||||
sects_by_id, sects_by_name = _load_sects()
|
||||
|
||||
|
||||
def get_sect_info_with_rank(avatar: "Avatar", detailed: bool = False) -> str:
|
||||
"""
|
||||
获取包含职位的宗门信息字符串
|
||||
|
||||
Args:
|
||||
avatar: 角色对象
|
||||
detailed: 是否包含宗门详细信息(阵营、风格、驻地等)
|
||||
|
||||
Returns:
|
||||
- 散修:返回"散修"
|
||||
- detailed=False:返回"明心剑宗长老"
|
||||
- detailed=True:返回"明心剑宗长老(阵营:正,风格:...,驻地:...)"
|
||||
"""
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
|
||||
# 散修直接返回
|
||||
if avatar.sect is None:
|
||||
return "散修"
|
||||
|
||||
# 获取职位+宗门名(如"明心剑宗长老")
|
||||
sect_rank_str = avatar.get_sect_str()
|
||||
|
||||
# 如果不需要详细信息,直接返回职位字符串
|
||||
if not detailed:
|
||||
return sect_rank_str
|
||||
|
||||
# 需要详细信息:拼接宗门的详细描述
|
||||
sect_detail = avatar.sect.get_detailed_info() # "明心剑宗(阵营:正,...)"
|
||||
|
||||
# 提取括号及其内容
|
||||
if "(" in sect_detail:
|
||||
detail_part = sect_detail[sect_detail.index("("):]
|
||||
return f"{sect_rank_str}{detail_part}"
|
||||
|
||||
# 如果没有括号(理论上不应该出现),直接返回职位字符串
|
||||
return sect_rank_str
|
||||
185
src/classes/sect_ranks.py
Normal file
185
src/classes/sect_ranks.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
宗门等级系统
|
||||
定义宗门内的职位等级及其与修仙境界的映射关系
|
||||
"""
|
||||
from enum import Enum
|
||||
from functools import total_ordering
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.cultivation import Realm
|
||||
from src.classes.sect import Sect
|
||||
|
||||
|
||||
@total_ordering
|
||||
class SectRank(Enum):
|
||||
"""
|
||||
宗门职位等级
|
||||
从高到低:掌门 > 长老 > 内门弟子 > 外门弟子
|
||||
"""
|
||||
Patriarch = "patriarch" # 掌门
|
||||
Elder = "elder" # 长老
|
||||
InnerDisciple = "inner" # 内门弟子
|
||||
OuterDisciple = "outer" # 外门弟子
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, SectRank):
|
||||
return NotImplemented
|
||||
# 数字越小职位越高,所以比较时反过来
|
||||
return RANK_ORDER[self] > RANK_ORDER[other]
|
||||
|
||||
def __le__(self, other):
|
||||
if not isinstance(other, SectRank):
|
||||
return NotImplemented
|
||||
return RANK_ORDER[self] >= RANK_ORDER[other]
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, SectRank):
|
||||
return NotImplemented
|
||||
# 数字越小职位越高,所以比较时反过来
|
||||
return RANK_ORDER[self] < RANK_ORDER[other]
|
||||
|
||||
def __ge__(self, other):
|
||||
if not isinstance(other, SectRank):
|
||||
return NotImplemented
|
||||
return RANK_ORDER[self] <= RANK_ORDER[other]
|
||||
|
||||
|
||||
# 宗门职位顺序(数字越小职位越高)
|
||||
RANK_ORDER = {
|
||||
SectRank.Patriarch: 0,
|
||||
SectRank.Elder: 1,
|
||||
SectRank.InnerDisciple: 2,
|
||||
SectRank.OuterDisciple: 3,
|
||||
}
|
||||
|
||||
|
||||
# 默认职位名称(可被宗门自定义覆盖)
|
||||
DEFAULT_RANK_NAMES = {
|
||||
SectRank.Patriarch: "掌门",
|
||||
SectRank.Elder: "长老",
|
||||
SectRank.InnerDisciple: "内门弟子",
|
||||
SectRank.OuterDisciple: "外门弟子",
|
||||
}
|
||||
|
||||
|
||||
def get_rank_from_realm(realm: "Realm") -> SectRank:
|
||||
"""
|
||||
根据修仙境界映射为宗门职位
|
||||
|
||||
映射规则:
|
||||
- 练气 → 外门弟子
|
||||
- 筑基 → 内门弟子
|
||||
- 金丹 → 长老
|
||||
- 元婴 → 掌门(需要额外检查唯一性)
|
||||
|
||||
Args:
|
||||
realm: 修仙境界
|
||||
|
||||
Returns:
|
||||
对应的宗门职位
|
||||
"""
|
||||
from src.classes.cultivation import Realm
|
||||
|
||||
mapping = {
|
||||
Realm.Qi_Refinement: SectRank.OuterDisciple,
|
||||
Realm.Foundation_Establishment: SectRank.InnerDisciple,
|
||||
Realm.Core_Formation: SectRank.Elder,
|
||||
Realm.Nascent_Soul: SectRank.Patriarch,
|
||||
}
|
||||
return mapping.get(realm, SectRank.OuterDisciple)
|
||||
|
||||
|
||||
def get_rank_display_name(rank: SectRank, sect: Optional["Sect"] = None) -> str:
|
||||
"""
|
||||
获取职位的显示名称(支持宗门自定义)
|
||||
|
||||
Args:
|
||||
rank: 宗门职位
|
||||
sect: 宗门对象(可选,如果提供则使用其自定义名称)
|
||||
|
||||
Returns:
|
||||
职位的显示名称
|
||||
"""
|
||||
if sect is not None:
|
||||
custom_name = sect.get_rank_name(rank)
|
||||
if custom_name:
|
||||
return custom_name
|
||||
return DEFAULT_RANK_NAMES.get(rank, "弟子")
|
||||
|
||||
|
||||
def should_auto_promote(old_realm: "Realm", new_realm: "Realm") -> bool:
|
||||
"""
|
||||
判断境界突破后是否应该自动晋升宗门职位
|
||||
|
||||
Args:
|
||||
old_realm: 旧境界
|
||||
new_realm: 新境界
|
||||
|
||||
Returns:
|
||||
是否应该晋升
|
||||
"""
|
||||
if old_realm == new_realm:
|
||||
return False
|
||||
|
||||
from src.classes.cultivation import Realm
|
||||
|
||||
# 检查境界是否提升
|
||||
old_rank = get_rank_from_realm(old_realm)
|
||||
new_rank = get_rank_from_realm(new_realm)
|
||||
|
||||
# 只有当新境界对应的职位更高时才晋升(职位枚举中 > 表示更高)
|
||||
return new_rank > old_rank
|
||||
|
||||
|
||||
def check_and_promote_sect_rank(avatar: "Avatar", old_realm: "Realm", new_realm: "Realm") -> None:
|
||||
"""
|
||||
检查境界突破后是否需要晋升宗门职位,并执行晋升
|
||||
|
||||
Args:
|
||||
avatar: 要检查的角色
|
||||
old_realm: 旧境界
|
||||
new_realm: 新境界
|
||||
"""
|
||||
# 无宗门或无职位,不需要晋升
|
||||
if avatar.sect is None or avatar.sect_rank is None:
|
||||
return
|
||||
|
||||
# 检查是否应该晋升
|
||||
if not should_auto_promote(old_realm, new_realm):
|
||||
return
|
||||
|
||||
new_rank = get_rank_from_realm(new_realm)
|
||||
|
||||
# 如果新职位是掌门,需要检查该宗门是否已有掌门
|
||||
if new_rank == SectRank.Patriarch:
|
||||
if sect_has_patriarch(avatar):
|
||||
# 已有掌门,只能晋升为长老
|
||||
new_rank = SectRank.Elder
|
||||
|
||||
# 执行晋升
|
||||
avatar.sect_rank = new_rank
|
||||
|
||||
|
||||
def sect_has_patriarch(avatar: "Avatar") -> bool:
|
||||
"""
|
||||
检查当前宗门是否已有掌门(不包括自己)
|
||||
|
||||
Args:
|
||||
avatar: 要检查的角色
|
||||
|
||||
Returns:
|
||||
是否已有其他掌门
|
||||
"""
|
||||
if avatar.sect is None:
|
||||
return False
|
||||
|
||||
# 从world中查找同宗门的其他avatar
|
||||
for other in avatar.world.avatar_manager.avatars.values():
|
||||
if other is avatar:
|
||||
continue
|
||||
if other.sect == avatar.sect and other.sect_rank == SectRank.Patriarch:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -209,6 +209,9 @@ def build_mortal_from_plan(world: World, current_month_stamp: MonthStamp, *, nam
|
||||
|
||||
# 位置刷新
|
||||
avatar.tile = world.map.get_tile(avatar.pos_x, avatar.pos_y)
|
||||
|
||||
# 分配宗门职位(根据境界)
|
||||
_assign_sect_rank(avatar, world)
|
||||
|
||||
# 写关系(父母/师徒);不发放宗门法宝
|
||||
if plan.parent_avatar is not None:
|
||||
@@ -446,6 +449,9 @@ def build_avatars_from_plan(
|
||||
|
||||
avatars_by_index[i] = avatar
|
||||
avatars_by_id[avatar.id] = avatar
|
||||
|
||||
# 批量分配宗门职位(需要在所有avatar创建后统一处理,以正确检查掌门唯一性)
|
||||
_assign_sect_ranks_batch(avatars_by_index, world)
|
||||
|
||||
for (a, b), relation in planned_relations.items():
|
||||
av_a = avatars_by_index[a]
|
||||
@@ -743,3 +749,82 @@ def get_new_avatar_with_config(
|
||||
|
||||
return avatar
|
||||
|
||||
|
||||
def _assign_sect_rank(avatar: Avatar, world: World) -> None:
|
||||
"""
|
||||
为单个avatar分配宗门职位(根据境界)
|
||||
处理掌门唯一性:如果该宗门已有掌门,元婴修士只能当长老
|
||||
|
||||
Args:
|
||||
avatar: 要分配职位的角色
|
||||
world: 世界对象
|
||||
"""
|
||||
# 散修无职位
|
||||
if avatar.sect is None:
|
||||
avatar.sect_rank = None
|
||||
return
|
||||
|
||||
from src.classes.sect_ranks import get_rank_from_realm, sect_has_patriarch, SectRank
|
||||
|
||||
# 根据境界获取对应职位
|
||||
rank = get_rank_from_realm(avatar.cultivation_progress.realm)
|
||||
|
||||
# 如果是掌门,检查该宗门是否已有掌门
|
||||
if rank == SectRank.Patriarch:
|
||||
if sect_has_patriarch(avatar):
|
||||
# 已有掌门,降为长老
|
||||
rank = SectRank.Elder
|
||||
|
||||
avatar.sect_rank = rank
|
||||
|
||||
|
||||
def _assign_sect_ranks_batch(avatars: List[Avatar], world: World) -> None:
|
||||
"""
|
||||
批量为avatars分配宗门职位
|
||||
确保每个宗门只有一个掌门(按境界等级优先,同境界随机)
|
||||
|
||||
Args:
|
||||
avatars: 要分配职位的角色列表
|
||||
world: 世界对象
|
||||
"""
|
||||
from src.classes.sect_ranks import get_rank_from_realm, SectRank
|
||||
|
||||
# 先为所有人分配基础职位
|
||||
for avatar in avatars:
|
||||
if avatar is None:
|
||||
continue
|
||||
if avatar.sect is None:
|
||||
avatar.sect_rank = None
|
||||
else:
|
||||
avatar.sect_rank = get_rank_from_realm(avatar.cultivation_progress.realm)
|
||||
|
||||
# 收集每个宗门的元婴修士(应为掌门的候选人)
|
||||
sect_nascent_souls: Dict[int, List[Avatar]] = {}
|
||||
for avatar in avatars:
|
||||
if avatar is None or avatar.sect is None:
|
||||
continue
|
||||
if avatar.sect_rank == SectRank.Patriarch:
|
||||
sect_id = avatar.sect.id
|
||||
if sect_id not in sect_nascent_souls:
|
||||
sect_nascent_souls[sect_id] = []
|
||||
sect_nascent_souls[sect_id].append(avatar)
|
||||
|
||||
# 检查world中已存在的掌门
|
||||
existing_patriarchs: Dict[int, bool] = {}
|
||||
for other in world.avatar_manager.avatars.values():
|
||||
if other.sect is not None and other.sect_rank == SectRank.Patriarch:
|
||||
existing_patriarchs[other.sect.id] = True
|
||||
|
||||
# 为每个宗门选择唯一掌门
|
||||
for sect_id, candidates in sect_nascent_souls.items():
|
||||
# 如果world中已有掌门,所有候选人都降为长老
|
||||
if existing_patriarchs.get(sect_id, False):
|
||||
for avatar in candidates:
|
||||
avatar.sect_rank = SectRank.Elder
|
||||
else:
|
||||
# 选择等级最高的作为掌门,其余降为长老
|
||||
candidates.sort(key=lambda av: av.cultivation_progress.level, reverse=True)
|
||||
# 第一个保持掌门
|
||||
for avatar in candidates[1:]:
|
||||
avatar.sect_rank = SectRank.Elder
|
||||
|
||||
|
||||
165
tests/test_sect_ranks.py
Normal file
165
tests/test_sect_ranks.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
测试宗门等级系统
|
||||
"""
|
||||
import pytest
|
||||
from src.classes.cultivation import CultivationProgress, Realm
|
||||
from src.classes.sect_ranks import (
|
||||
SectRank,
|
||||
get_rank_from_realm,
|
||||
get_rank_display_name,
|
||||
should_auto_promote,
|
||||
)
|
||||
from src.classes.sect import sects_by_name
|
||||
from src.classes.world import World
|
||||
from src.classes.map import Map
|
||||
from src.classes.calendar import MonthStamp
|
||||
from src.sim.new_avatar import make_avatars
|
||||
|
||||
|
||||
def test_rank_from_realm():
|
||||
"""测试境界到宗门职位的映射"""
|
||||
assert get_rank_from_realm(Realm.Qi_Refinement) == SectRank.OuterDisciple
|
||||
assert get_rank_from_realm(Realm.Foundation_Establishment) == SectRank.InnerDisciple
|
||||
assert get_rank_from_realm(Realm.Core_Formation) == SectRank.Elder
|
||||
assert get_rank_from_realm(Realm.Nascent_Soul) == SectRank.Patriarch
|
||||
|
||||
|
||||
def test_rank_display_name():
|
||||
"""测试职位显示名称"""
|
||||
assert get_rank_display_name(SectRank.Patriarch) == "掌门"
|
||||
assert get_rank_display_name(SectRank.Elder) == "长老"
|
||||
assert get_rank_display_name(SectRank.InnerDisciple) == "内门弟子"
|
||||
assert get_rank_display_name(SectRank.OuterDisciple) == "外门弟子"
|
||||
|
||||
|
||||
def test_rank_comparison():
|
||||
"""测试职位比较"""
|
||||
assert SectRank.Patriarch > SectRank.Elder
|
||||
assert SectRank.Elder > SectRank.InnerDisciple
|
||||
assert SectRank.InnerDisciple > SectRank.OuterDisciple
|
||||
assert SectRank.OuterDisciple < SectRank.Patriarch
|
||||
|
||||
|
||||
def test_auto_promote():
|
||||
"""测试自动晋升逻辑"""
|
||||
assert should_auto_promote(Realm.Qi_Refinement, Realm.Foundation_Establishment) == True
|
||||
assert should_auto_promote(Realm.Foundation_Establishment, Realm.Core_Formation) == True
|
||||
assert should_auto_promote(Realm.Core_Formation, Realm.Nascent_Soul) == True
|
||||
assert should_auto_promote(Realm.Qi_Refinement, Realm.Qi_Refinement) == False
|
||||
|
||||
|
||||
def test_avatar_sect_rank_assignment():
|
||||
"""测试avatar创建时宗门职位分配"""
|
||||
from src.run.create_map import create_cultivation_world_map
|
||||
game_map = create_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
# 创建多个avatar
|
||||
avatars_dict = make_avatars(world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 检查所有有宗门的avatar都有职位
|
||||
for avatar in avatars:
|
||||
if avatar.sect is not None:
|
||||
assert avatar.sect_rank is not None, f"{avatar.name} 有宗门但没有职位"
|
||||
# 职位应该匹配境界
|
||||
expected_rank = get_rank_from_realm(avatar.cultivation_progress.realm)
|
||||
# 如果不是预期职位,只可能是元婴修士被降为长老(因为掌门唯一性)
|
||||
if avatar.sect_rank != expected_rank:
|
||||
assert avatar.cultivation_progress.realm == Realm.Nascent_Soul
|
||||
assert avatar.sect_rank == SectRank.Elder
|
||||
else:
|
||||
assert avatar.sect_rank is None, f"{avatar.name} 散修不应该有职位"
|
||||
|
||||
|
||||
def test_patriarch_uniqueness():
|
||||
"""测试每个宗门只有一个掌门"""
|
||||
from src.run.create_map import create_cultivation_world_map
|
||||
game_map = create_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
# 创建足够多的avatar
|
||||
avatars_dict = make_avatars(world, count=50, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 统计每个宗门的掌门数量
|
||||
sect_patriarchs = {}
|
||||
for avatar in avatars:
|
||||
if avatar.sect is not None and avatar.sect_rank == SectRank.Patriarch:
|
||||
sect_id = avatar.sect.id
|
||||
if sect_id not in sect_patriarchs:
|
||||
sect_patriarchs[sect_id] = []
|
||||
sect_patriarchs[sect_id].append(avatar.name)
|
||||
|
||||
# 确保每个宗门最多只有一个掌门
|
||||
for sect_id, patriarchs in sect_patriarchs.items():
|
||||
assert len(patriarchs) <= 1, f"宗门 {sect_id} 有多个掌门: {patriarchs}"
|
||||
|
||||
|
||||
def test_sect_str_display():
|
||||
"""测试宗门信息显示"""
|
||||
from src.run.create_map import create_cultivation_world_map
|
||||
game_map = create_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
avatars_dict = make_avatars(world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
for avatar in avatars:
|
||||
sect_str = avatar.get_sect_str()
|
||||
if avatar.sect is None:
|
||||
assert sect_str == "散修"
|
||||
else:
|
||||
# 应该包含宗门名
|
||||
assert avatar.sect.name in sect_str
|
||||
# 应该包含职位名
|
||||
if avatar.sect_rank is not None:
|
||||
rank_name = get_rank_display_name(avatar.sect_rank, avatar.sect)
|
||||
assert rank_name in sect_str
|
||||
|
||||
|
||||
def test_cultivation_breakthrough_promotion():
|
||||
"""测试突破境界后自动晋升"""
|
||||
from src.run.create_map import create_cultivation_world_map
|
||||
game_map = create_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
avatars_dict = make_avatars(world, count=10, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 找一个练气期的宗门弟子
|
||||
target_avatar = None
|
||||
for avatar in avatars:
|
||||
if avatar.sect is not None and avatar.cultivation_progress.realm == Realm.Qi_Refinement:
|
||||
target_avatar = avatar
|
||||
break
|
||||
|
||||
if target_avatar is not None:
|
||||
# 记录原职位
|
||||
old_rank = target_avatar.sect_rank
|
||||
assert old_rank == SectRank.OuterDisciple
|
||||
|
||||
# 突破到筑基
|
||||
target_avatar.cultivation_progress.level = 31 # 筑基初期
|
||||
target_avatar.update_cultivation(31)
|
||||
|
||||
# 检查职位是否晋升
|
||||
assert target_avatar.sect_rank == SectRank.InnerDisciple
|
||||
assert target_avatar.sect_rank > old_rank
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
||||
Reference in New Issue
Block a user