diff --git a/src/classes/action.py b/src/classes/action.py index 04dfd07..96fd31a 100644 --- a/src/classes/action.py +++ b/src/classes/action.py @@ -6,7 +6,7 @@ import json import inspect from src.classes.essence import Essence, EssenceType -from src.classes.root import Root, corres_essence_type +from src.classes.root import Root, get_essence_types_for_root from src.classes.region import Region, CultivateRegion, NormalRegion, CityRegion from src.classes.event import Event, NULL_EVENT from src.classes.item import Item, items_by_name @@ -286,7 +286,7 @@ class Cultivate(DefineAction, ActualActionMixin): 修炼动作,可以增加修仙进度。 """ COMMENT = "修炼,增进修为" - DOABLES_REQUIREMENTS = "在修炼区域中,角色不可以突破" + DOABLES_REQUIREMENTS = "在修炼区域中,修炼区域的灵气为角色的灵根之一,且角色未到瓶颈。" PARAMS = {} def _execute(self) -> None: """ @@ -295,8 +295,9 @@ class Cultivate(DefineAction, ActualActionMixin): """ root = self.avatar.root essence = self.avatar.tile.region.essence - essence_type = corres_essence_type[root] - essence_density = essence.get_density(essence_type) + # 多元素:取与角色灵根任一匹配元素的最大密度 + essence_types = get_essence_types_for_root(root) + essence_density = max((essence.get_density(et) for et in essence_types), default=0) exp = self.get_exp(essence_density) self.avatar.cultivation_progress.add_exp(exp) @@ -321,8 +322,14 @@ class Cultivate(DefineAction, ActualActionMixin): """ root = self.avatar.root region = self.avatar.tile.region - _corres_essence_type = corres_essence_type[root] - return self.avatar.cultivation_progress.can_cultivate() and isinstance(region, CultivateRegion) and region.essence.get_density(_corres_essence_type) > 0 + essence_types = get_essence_types_for_root(root) + if not self.avatar.cultivation_progress.can_cultivate(): + return False + if not isinstance(region, CultivateRegion): + return False + if all(region.essence.get_density(et) == 0 for et in essence_types): + return False + return True # 突破境界class @long_action(step_month=1) @@ -331,7 +338,7 @@ class Breakthrough(DefineAction, ActualActionMixin): 突破境界 """ COMMENT = "尝试突破境界" - DOABLES_REQUIREMENTS = "角色可以突破时" + DOABLES_REQUIREMENTS = "角色处于瓶颈时" PARAMS = {} def calc_success_rate(self) -> float: """ diff --git a/src/classes/ai.py b/src/classes/ai.py index 0306aed..ede0702 100644 --- a/src/classes/ai.py +++ b/src/classes/ai.py @@ -10,7 +10,7 @@ import random from src.classes.world import World from src.classes.region import Region -from src.classes.root import corres_essence_type +from src.classes.root import get_essence_types_for_root from src.classes.event import Event, NULL_EVENT from src.utils.llm import get_ai_prompt_and_call_llm_async from src.classes.typings import ACTION_NAME, ACTION_PARAMS, ACTION_PAIR, ACTION_NAME_PARAMS_PAIRS @@ -102,10 +102,10 @@ class RuleAI(AI): """ 根据avatar的灵根找到最适合的区域 """ - essence_type = corres_essence_type[avatar.root] - region_with_best_essence = max( - regions, key=lambda region: region.essence.get_density(essence_type) - ) + essence_types = get_essence_types_for_root(avatar.root) + def best_density(region: Region) -> int: + return max((region.essence.get_density(et) for et in essence_types), default=0) + region_with_best_essence = max(regions, key=best_density) return region_with_best_essence class LLMAI(AI): diff --git a/src/classes/avatar.py b/src/classes/avatar.py index f9a42e4..eb65502 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -97,7 +97,7 @@ class Avatar: 尽量多打一些,因为会用来给LLM进行决策 """ personas_str = ", ".join([persona.name for persona in self.personas]) - return f"Avatar(id={self.id}, 性别={self.gender}, 年龄={self.age}, name={self.name}, 区域={self.tile.region.name}, 灵根={self.root.value}, 境界={self.cultivation_progress}, HP={self.hp}, MP={self.mp}, 个性={personas_str})" + return f"Avatar(id={self.id}, 性别={self.gender}, 年龄={self.age}, name={self.name}, 区域={self.tile.region.name}, 灵根={str(self.root)}, 境界={self.cultivation_progress}, HP={self.hp}, MP={self.mp}, 个性={personas_str})" def __str__(self) -> str: return self.get_info() diff --git a/src/classes/cultivation.py b/src/classes/cultivation.py index dbb845a..6bfe7f6 100644 --- a/src/classes/cultivation.py +++ b/src/classes/cultivation.py @@ -1,11 +1,37 @@ from enum import Enum +from functools import total_ordering +@total_ordering class Realm(Enum): Qi_Refinement = "练气" Foundation_Establishment = "筑基" Core_Formation = "金丹" Nascent_Soul = "元婴" + @classmethod + def from_id(cls, realm_id: int) -> "Realm": + order: tuple[Realm, ...] = ( + cls.Qi_Refinement, + cls.Foundation_Establishment, + cls.Core_Formation, + cls.Nascent_Soul, + ) + index = realm_id - 1 + if index < 0 or index >= len(order): + raise ValueError(f"Unknown realm_id: {realm_id}") + return order[index] + + def __lt__(self, other): + if not isinstance(other, Realm): + return NotImplemented + order: tuple[Realm, ...] = ( + Realm.Qi_Refinement, + Realm.Foundation_Establishment, + Realm.Core_Formation, + Realm.Nascent_Soul, + ) + return order.index(self) < order.index(other) + class Stage(Enum): Early_Stage = "前期" Middle_Stage = "中期" @@ -26,14 +52,6 @@ LEVEL_TO_STAGE = { 20: Stage.Late_Stage, } -# realm_id到Realm的映射(用于物品等级系统) -REALM_ID_TO_REALM = { - 1: Realm.Qi_Refinement, - 2: Realm.Foundation_Establishment, - 3: Realm.Core_Formation, - 4: Realm.Nascent_Soul, -} - LEVEL_TO_BREAK_THROUGH = { 30: Realm.Foundation_Establishment, 60: Realm.Core_Formation, @@ -165,18 +183,25 @@ class CultivationProgress: self.realm = self.get_realm(self.level) self.stage = self.get_stage(self.level) + def is_in_bottleneck(self) -> bool: + """ + 检查是否可以突破 + 其实就是再瓶颈期间。 + """ + return self.level in LEVEL_TO_BREAK_THROUGH.keys() + def can_break_through(self) -> bool: """ 检查是否可以突破 """ - return self.level in LEVEL_TO_BREAK_THROUGH.keys() + return self.is_in_bottleneck() def can_cultivate(self) -> bool: """ 检查是否可以修炼 可以突破,说明到顶了,说明不能修炼了,必须突破后才能正常修炼。 """ - return not self.can_break_through() + return not self.is_in_bottleneck() def is_level_up(self) -> bool: """ @@ -186,65 +211,20 @@ class CultivationProgress: return self.exp >= exp_required def __str__(self) -> str: - return f"{self.realm.value}{self.stage.value}({self.level}级)。可以突破:{self.can_break_through()}" + return f"{self.realm.value}{self.stage.value}({self.level}级)。在瓶颈期:{self.is_in_bottleneck()}" -# 为Realm类添加from_id类方法 -def _realm_from_id(cls, realm_id: int) -> Realm: - """ - 根据realm_id获取对应的Realm - - Args: - realm_id: 境界ID (1-4) - - Returns: - 对应的Realm枚举值 - - Raises: - ValueError: 如果realm_id不存在 - """ - if realm_id not in REALM_ID_TO_REALM: - raise ValueError(f"Unknown realm_id: {realm_id}") - return REALM_ID_TO_REALM[realm_id] -# 将from_id方法绑定到Realm类 -Realm.from_id = classmethod(_realm_from_id) - -# 境界顺序映射 -_REALM_ORDER = { - Realm.Qi_Refinement: 1, - Realm.Foundation_Establishment: 2, - Realm.Core_Formation: 3, - Realm.Nascent_Soul: 4, +breakthrough_success_rate_by_realm = { + Realm.Qi_Refinement: 0.8, + Realm.Foundation_Establishment: 0.6, + Realm.Core_Formation: 0.4, + Realm.Nascent_Soul: 0.2, } -# 为Realm类添加比较操作符 -def _realm_ge(self, other): - """大于等于比较""" - if not isinstance(other, Realm): - return NotImplemented - return _REALM_ORDER[self] >= _REALM_ORDER[other] - -def _realm_le(self, other): - """小于等于比较""" - if not isinstance(other, Realm): - return NotImplemented - return _REALM_ORDER[self] <= _REALM_ORDER[other] - -def _realm_gt(self, other): - """大于比较""" - if not isinstance(other, Realm): - return NotImplemented - return _REALM_ORDER[self] > _REALM_ORDER[other] - -def _realm_lt(self, other): - """小于比较""" - if not isinstance(other, Realm): - return NotImplemented - return _REALM_ORDER[self] < _REALM_ORDER[other] - -# 将比较方法绑定到Realm类 -Realm.__ge__ = _realm_ge -Realm.__le__ = _realm_le -Realm.__gt__ = _realm_gt -Realm.__lt__ = _realm_lt \ No newline at end of file +breakthrough_fail_reduce_lifespan_by_realm = { + Realm.Qi_Refinement: 10, + Realm.Foundation_Establishment: 20, + Realm.Core_Formation: 30, + Realm.Nascent_Soul: 40, +} \ No newline at end of file diff --git a/src/classes/root.py b/src/classes/root.py index b77112a..1e1e6b4 100644 --- a/src/classes/root.py +++ b/src/classes/root.py @@ -1,47 +1,90 @@ """ 灵根 -目前只有五行:金木水火土。 -其实和EssenceType很类似 -但是单独拿出来是因为,之后可能整特殊的复杂灵根 -所以这里单独定义一个Root类,用来描述灵根。 +五行元素与灵根组合: +- RootElement:金、木、水、火、土 +- Root:由一个或多个 RootElement 组成(单灵根、双灵根、天灵根) """ from enum import Enum +from typing import List, Tuple from src.classes.essence import EssenceType -class Root(Enum): - """ - 灵根 - """ + +class RootElement(Enum): GOLD = "金" WOOD = "木" WATER = "水" FIRE = "火" EARTH = "土" - - @classmethod - def from_str(cls, root_str: str) -> 'Root': - """ - 从字符串创建Root实例 - - Args: - root_str: 灵根的字符串表示,如 "金", "木", "水", "火", "土" - - Returns: - 对应的Root枚举值 - - Raises: - ValueError: 如果字符串不匹配任何已知的灵根类型 - """ - for root in cls: - if root.value == root_str: - return root - raise ValueError(f"Unknown root type: {root_str}") -corres_essence_type = { - Root.GOLD: EssenceType.GOLD, - Root.WOOD: EssenceType.WOOD, - Root.WATER: EssenceType.WATER, - Root.FIRE: EssenceType.FIRE, - Root.EARTH: EssenceType.EARTH, + def __str__(self) -> str: + return self.value + + +class Root(Enum): + """ + 复合灵根定义:每个成员包含(中文名, 元素列表) + """ + GOLD = ("金灵根", (RootElement.GOLD,)) + WOOD = ("木灵根", (RootElement.WOOD,)) + WATER = ("水灵根", (RootElement.WATER,)) + FIRE = ("火灵根", (RootElement.FIRE,)) + EARTH = ("土灵根", (RootElement.EARTH,)) + + THUNDER = ("雷灵根", (RootElement.WATER, RootElement.EARTH)) # 水+土 + ICE = ("冰灵根", (RootElement.GOLD, RootElement.WATER)) # 金+水 + WIND = ("风灵根", (RootElement.WOOD, RootElement.WATER)) # 木+水 + DARK = ("暗灵根", (RootElement.FIRE, RootElement.EARTH)) # 火+土 + + HEAVEN = ( + "天灵根", + ( + RootElement.GOLD, + RootElement.WOOD, + RootElement.WATER, + RootElement.FIRE, + RootElement.EARTH, + ), + ) + + def __new__(cls, cn_name: str, elements: Tuple[RootElement, ...]): + obj = object.__new__(cls) + obj._value_ = cn_name + obj._elements = elements + return obj + + @property + def elements(self) -> Tuple[RootElement, ...]: + return self._elements + + def __str__(self) -> str: + return f"{self.value}({', '.join(str(e) for e in self.elements)})" + +# 元素到灵气类型的一一对应 +_essence_by_element = { + RootElement.GOLD: EssenceType.GOLD, + RootElement.WOOD: EssenceType.WOOD, + RootElement.WATER: EssenceType.WATER, + RootElement.FIRE: EssenceType.FIRE, + RootElement.EARTH: EssenceType.EARTH, +} + + +def get_essence_types_for_root(root: Root) -> List[EssenceType]: + """ + 获取与该灵根匹配的灵气类型列表(任一元素匹配即视为可用)。 + """ + return [_essence_by_element[e] for e in root.elements] + +roots = { + "金": Root.GOLD, + "木": Root.WOOD, + "水": Root.WATER, + "火": Root.FIRE, + "土": Root.EARTH, + "雷": Root.THUNDER, + "冰": Root.ICE, + "风": Root.WIND, + "暗": Root.DARK, + "天": Root.HEAVEN, } \ No newline at end of file diff --git a/src/front/rendering.py b/src/front/rendering.py index 451198b..bb61f82 100644 --- a/src/front/rendering.py +++ b/src/front/rendering.py @@ -144,7 +144,7 @@ def draw_tooltip_for_avatar(pygame_mod, screen, colors, font, avatar: Avatar): f"境界: {str(avatar.cultivation_progress)}", f"HP: {avatar.hp}", f"MP: {avatar.mp}", - f"灵根: {avatar.root.value}", + f"灵根: {str(avatar.root)}", f"个性: {', '.join([persona.name for persona in avatar.personas])}", f"位置: ({avatar.pos_x}, {avatar.pos_y})", ] diff --git a/static/config.yml b/static/config.yml index 157a06b..66e9579 100644 --- a/static/config.yml +++ b/static/config.yml @@ -12,7 +12,7 @@ ai: max_decide_num: 3 game: - init_npc_num: 4 + init_npc_num: 2 npc_birth_rate_per_month: 0.001 df: