181 lines
6.0 KiB
Python
181 lines
6.0 KiB
Python
from __future__ import annotations
|
||
|
||
import random
|
||
from typing import Optional, TYPE_CHECKING, List
|
||
|
||
from src.classes.action import TimedAction
|
||
from src.classes.cultivation import Realm
|
||
from src.classes.event import Event
|
||
from src.classes.item import Item
|
||
from src.classes.weapon import get_random_weapon_by_realm
|
||
from src.classes.auxiliary import get_random_auxiliary_by_realm
|
||
from src.classes.single_choice import handle_item_exchange
|
||
from src.utils.resolution import resolve_query
|
||
|
||
if TYPE_CHECKING:
|
||
from src.classes.avatar import Avatar
|
||
|
||
class Cast(TimedAction):
|
||
"""
|
||
铸造动作:消耗同阶材料,尝试打造同阶宝物(兵器或辅助装备)。
|
||
持续时间:3个月
|
||
"""
|
||
ACTION_NAME = "铸造"
|
||
EMOJI = "🔥"
|
||
DESC = "消耗材料尝试铸造法宝"
|
||
|
||
COST = 5
|
||
SUCCESS_RATES = {
|
||
Realm.Qi_Refinement: 0.4,
|
||
Realm.Foundation_Establishment: 0.3,
|
||
Realm.Core_Formation: 0.2,
|
||
Realm.Nascent_Soul: 0.1,
|
||
}
|
||
|
||
DOABLES_REQUIREMENTS = f"拥有{COST}个同境界材料"
|
||
PARAMS = {"target_realm": "目标境界名称('练气'、'筑基'、'金丹'、'元婴')"}
|
||
IS_MAJOR = False
|
||
|
||
duration_months = 3
|
||
|
||
def __init__(self, avatar: Avatar, world):
|
||
super().__init__(avatar, world)
|
||
self.target_realm: Optional[Realm] = None
|
||
|
||
def _get_cost(self) -> int:
|
||
return self.COST
|
||
|
||
def _count_materials(self, realm: Realm) -> int:
|
||
"""
|
||
统计符合条件的材料数量。
|
||
注意:仅统计 Item 类的直接实例,不统计 Weapon/Auxiliary 等子类(它们也是 Item,但通常不作为铸造原材料)。
|
||
"""
|
||
count = 0
|
||
for item, qty in self.avatar.items.items():
|
||
# 这里使用 type(item) is Item 来严格限制必须是基础材料
|
||
# 如果项目里有其他继承自 Item 的材料类,可能需要放宽这个限制
|
||
if type(item).__name__ == "Item" and item.realm == realm:
|
||
count += qty
|
||
return count
|
||
|
||
def can_start(self, target_realm: str) -> tuple[bool, str]:
|
||
if not target_realm:
|
||
return False, "未指定目标境界"
|
||
|
||
res = resolve_query(target_realm, expected_types=[Realm])
|
||
if not res.is_valid:
|
||
return False, f"无效的境界: {target_realm}"
|
||
|
||
realm = res.obj
|
||
|
||
cost = self._get_cost()
|
||
count = self._count_materials(realm)
|
||
|
||
if count < cost:
|
||
return False, f"材料不足,需要 {cost} 个{target_realm}阶材料,当前拥有 {count} 个"
|
||
|
||
return True, ""
|
||
|
||
def start(self, target_realm: str) -> Event:
|
||
res = resolve_query(target_realm, expected_types=[Realm])
|
||
if res.is_valid:
|
||
self.target_realm = res.obj
|
||
self.target_realm = Realm(target_realm)
|
||
|
||
cost = self._get_cost()
|
||
|
||
# 扣除材料逻辑
|
||
to_deduct = cost
|
||
items_to_modify = []
|
||
|
||
# 再次遍历寻找材料进行扣除
|
||
for item, qty in self.avatar.items.items():
|
||
if to_deduct <= 0:
|
||
break
|
||
if type(item).__name__ == "Item" and item.realm == self.target_realm:
|
||
take = min(qty, to_deduct)
|
||
items_to_modify.append((item, take))
|
||
to_deduct -= take
|
||
|
||
for item, take in items_to_modify:
|
||
self.avatar.remove_item(item, take)
|
||
|
||
realm_val = self.target_realm.value if self.target_realm else target_realm
|
||
return Event(
|
||
self.world.month_stamp,
|
||
f"{self.avatar.name} 开始尝试铸造{realm_val}阶法宝。",
|
||
related_avatars=[self.avatar.id]
|
||
)
|
||
|
||
def _execute(self) -> None:
|
||
# 持续过程中无特殊逻辑
|
||
pass
|
||
|
||
async def finish(self) -> list[Event]:
|
||
if self.target_realm is None:
|
||
return []
|
||
|
||
# 1. 计算成功率
|
||
base_rate = self.SUCCESS_RATES.get(self.target_realm, 0.1)
|
||
extra_rate = float(self.avatar.effects.get("extra_cast_success_rate", 0.0))
|
||
success_rate = base_rate + extra_rate
|
||
|
||
events = []
|
||
|
||
# 2. 判定结果
|
||
if random.random() > success_rate:
|
||
# 失败
|
||
fail_event = Event(
|
||
self.world.month_stamp,
|
||
f"{self.avatar.name} 铸造{self.target_realm.value}阶法宝失败,所有材料化为灰烬。",
|
||
related_avatars=[self.avatar.id],
|
||
is_major=False
|
||
)
|
||
events.append(fail_event)
|
||
return events
|
||
|
||
# 3. 成功:生成物品
|
||
# 50% 兵器,50% 辅助装备
|
||
is_weapon = random.random() < 0.5
|
||
new_item = None
|
||
item_type = ""
|
||
item_label = ""
|
||
|
||
if is_weapon:
|
||
new_item = get_random_weapon_by_realm(self.target_realm)
|
||
item_type = "weapon"
|
||
item_label = "兵器"
|
||
else:
|
||
new_item = get_random_auxiliary_by_realm(self.target_realm)
|
||
item_type = "auxiliary"
|
||
item_label = "辅助装备"
|
||
|
||
# 4. 决策:保留还是卖出
|
||
base_desc = f"铸造成功!获得了{self.target_realm.value}{item_label}『{new_item.name}』。"
|
||
|
||
# 事件1:铸造成功
|
||
events.append(Event(
|
||
self.world.month_stamp,
|
||
f"{self.avatar.name} 成功铸造{self.target_realm.value}{item_label}『{new_item.name}』。",
|
||
related_avatars=[self.avatar.id],
|
||
is_major=True
|
||
))
|
||
|
||
_, result_text = await handle_item_exchange(
|
||
avatar=self.avatar,
|
||
new_item=new_item,
|
||
item_type=item_type,
|
||
context_intro=base_desc,
|
||
can_sell_new=True
|
||
)
|
||
|
||
# 事件2:处置结果
|
||
events.append(Event(
|
||
self.world.month_stamp,
|
||
result_text,
|
||
related_avatars=[self.avatar.id],
|
||
is_major=True
|
||
))
|
||
|
||
return events
|