finish animal and plants

This commit is contained in:
bridge
2025-09-11 23:21:06 +08:00
parent f2938bf8d8
commit ccdcc590f4
15 changed files with 412 additions and 50 deletions

View File

@@ -7,8 +7,9 @@ import inspect
from src.classes.essence import Essence, EssenceType
from src.classes.root import Root, corres_essence_type
from src.classes.region import Region, CultivateRegion
from src.classes.region import Region, CultivateRegion, NormalRegion
from src.classes.event import Event, NULL_EVENT
from src.classes.item import Item
if TYPE_CHECKING:
from src.classes.avatar import Avatar
@@ -319,7 +320,118 @@ class Play(DefineAction, ActualActionMixin):
def is_doable(self) -> bool:
return True
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play]
ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play]
ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play"]
ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play"]
@long_action(step_month=6)
class Hunt(DefineAction, ActualActionMixin):
"""
狩猎动作在有动物的区域进行狩猎持续6个月
可以获得动物对应的物品
"""
COMMENT = "在当前区域狩猎动物,获取动物材料"
PARAMS = {}
def _execute(self) -> None:
"""
执行狩猎动作
"""
region = self.avatar.tile.region
success_rate = self.get_success_rate()
if random.random() < success_rate:
# 成功狩猎从avatar境界足够的动物中随机选择一种
avatar_realm = self.avatar.cultivation_progress.realm
available_animals = [animal for animal in region.animals if avatar_realm >= animal.realm]
target_animal = random.choice(available_animals)
# 随机选择该动物的一种物品
item = random.choice(target_animal.items)
self.avatar.add_item(item, 1)
def get_success_rate(self) -> float:
"""
获取狩猎成功率预留接口目前固定为100%
"""
return 1.0 # 100%成功率
def get_event(self) -> Event:
"""
获取狩猎动作开始时的事件
"""
region = self.avatar.tile.region
return Event(self.world.month_stamp, f"{self.avatar.name}{region.name} 开始狩猎")
@property
def is_doable(self) -> bool:
"""
判断是否可以狩猎必须在有动物的普通区域且avatar的境界必须大于等于动物的境界
"""
region = self.avatar.tile.region
if not isinstance(region, NormalRegion) or len(region.animals) == 0:
return False
# 检查avatar的境界是否足够狩猎区域内的动物
avatar_realm = self.avatar.cultivation_progress.realm
for animal in region.animals:
if avatar_realm >= animal.realm:
return True
return False
@long_action(step_month=6)
class Harvest(DefineAction, ActualActionMixin):
"""
采集动作在有植物的区域进行采集持续6个月
可以获得植物对应的物品
"""
COMMENT = "在当前区域采集植物,获取植物材料"
PARAMS = {}
def _execute(self) -> None:
"""
执行采集动作
"""
region = self.avatar.tile.region
success_rate = self.get_success_rate()
if random.random() < success_rate:
# 成功采集从avatar境界足够的植物中随机选择一种
avatar_realm = self.avatar.cultivation_progress.realm
available_plants = [plant for plant in region.plants if avatar_realm >= plant.realm]
target_plant = random.choice(available_plants)
# 随机选择该植物的一种物品
item = random.choice(target_plant.items)
self.avatar.add_item(item, 1)
def get_success_rate(self) -> float:
"""
获取采集成功率预留接口目前固定为100%
"""
return 1.0 # 100%成功率
def get_event(self) -> Event:
"""
获取采集动作开始时的事件
"""
region = self.avatar.tile.region
return Event(self.world.month_stamp, f"{self.avatar.name}{region.name} 开始采集")
@property
def is_doable(self) -> bool:
"""
判断是否可以采集必须在有植物的普通区域且avatar的境界必须大于等于植物的境界
"""
region = self.avatar.tile.region
if not isinstance(region, NormalRegion) or len(region.plants) == 0:
return False
# 检查avatar的境界是否足够采集区域内的植物
avatar_realm = self.avatar.cultivation_progress.realm
for plant in region.plants:
if avatar_realm >= plant.realm:
return True
return False
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest]
ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest]
ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest"]
ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest"]

View File

@@ -1,6 +1,10 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Optional
from src.utils.df import game_configs
from src.utils.config import CONFIG
from src.classes.item import Item, items_by_id
from src.classes.cultivation import Realm
@dataclass
class Animal:
@@ -10,13 +14,35 @@ class Animal:
id: int
name: str
desc: str
grade: int
realm: Realm
item_ids: list[int] = field(default_factory=list) # 该动物对应的物品IDs
# 这些字段将在__post_init__中设置
items: list[Item] = field(init=False, default_factory=list) # 该动物对应的物品实例
def __post_init__(self):
"""初始化物品实例"""
for item_id in self.item_ids:
if item_id in items_by_id:
self.items.append(items_by_id[item_id])
def __hash__(self) -> int:
return hash(self.id)
def __str__(self) -> str:
return self.name
def get_info(self) -> str:
"""
获取动物的详细信息,包括名字、描述、境界和材料
"""
info_parts = [f"{self.name}】({self.realm.value})", self.desc]
if self.items:
item_names = [item.name for item in self.items]
info_parts.append(f"可获得材料:{', '.join(item_names)}")
return " - ".join(info_parts)
def _load_animals() -> tuple[dict[int, Animal], dict[str, Animal]]:
"""从配表加载animal数据"""
@@ -25,11 +51,19 @@ def _load_animals() -> tuple[dict[int, Animal], dict[str, Animal]]:
animal_df = game_configs["animal"]
for _, row in animal_df.iterrows():
# 处理item_ids
item_ids_list = []
item_ids = row.get("item_ids")
if item_ids is not None and str(item_ids).strip() and str(item_ids) != 'nan':
for item_id_str in str(item_ids).split(CONFIG.df.ids_separator):
item_ids_list.append(int(float(item_id_str.strip())))
animal = Animal(
id=int(row["id"]),
name=str(row["name"]),
desc=str(row["desc"]),
grade=int(row["grade"])
realm=Realm.from_id(int(row["stage_id"])),
item_ids=item_ids_list
)
animals_by_id[animal.id] = animal
animals_by_name[animal.name] = animal

View File

@@ -26,6 +26,14 @@ 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,
@@ -57,9 +65,9 @@ class CultivationProgress:
return realm
return Realm.Qi_Refinement
def get_stage(self, level: int) -> str:
def get_stage(self, level: int) -> Stage:
"""获取阶段"""
_level = self.level % levels_per_realm
_level = level % levels_per_realm
for level_threshold, stage in reversed(list(level_to_stage.items())):
if _level >= level_threshold:
return stage
@@ -155,4 +163,65 @@ 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.can_break_through()}"
# 为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,
}
# 为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

View File

@@ -1,6 +1,7 @@
from dataclasses import dataclass
from src.utils.df import game_configs
from src.classes.cultivation import Realm
@dataclass
class Item:
@@ -10,7 +11,7 @@ class Item:
id: int
name: str
desc: str
grade: int
realm: Realm
def __hash__(self) -> int:
return hash(self.id)
@@ -29,7 +30,7 @@ def _load_items() -> tuple[dict[int, Item], dict[str, Item]]:
id=int(row["id"]),
name=str(row["name"]),
desc=str(row["desc"]),
grade=int(row["grade"])
realm=Realm.from_id(int(row["stage_id"]))
)
items_by_id[item.id] = item
items_by_name[item.name] = item

View File

@@ -1,6 +1,10 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Optional
from src.utils.df import game_configs
from src.utils.config import CONFIG
from src.classes.item import Item, items_by_id
from src.classes.cultivation import Realm
@dataclass
class Plant:
@@ -10,13 +14,35 @@ class Plant:
id: int
name: str
desc: str
grade: int
realm: Realm
item_ids: list[int] = field(default_factory=list) # 该植物对应的物品IDs
# 这些字段将在__post_init__中设置
items: list[Item] = field(init=False, default_factory=list) # 该植物对应的物品实例
def __post_init__(self):
"""初始化物品实例"""
for item_id in self.item_ids:
if item_id in items_by_id:
self.items.append(items_by_id[item_id])
def __hash__(self) -> int:
return hash(self.id)
def __str__(self) -> str:
return self.name
def get_info(self) -> str:
"""
获取植物的详细信息,包括名字、描述、境界和材料
"""
info_parts = [f"{self.name}】({self.realm.value})", self.desc]
if self.items:
item_names = [item.name for item in self.items]
info_parts.append(f"可获得材料:{', '.join(item_names)}")
return " - ".join(info_parts)
def _load_plants() -> tuple[dict[int, Plant], dict[str, Plant]]:
"""从配表加载plant数据"""
@@ -25,11 +51,19 @@ def _load_plants() -> tuple[dict[int, Plant], dict[str, Plant]]:
plant_df = game_configs["plant"]
for _, row in plant_df.iterrows():
# 处理item_ids
item_ids_list = []
item_ids = row.get("item_ids")
if item_ids is not None and str(item_ids).strip() and str(item_ids) != 'nan':
for item_id_str in str(item_ids).split(CONFIG.df.ids_separator):
item_ids_list.append(int(float(item_id_str.strip())))
plant = Plant(
id=int(row["id"]),
name=str(row["name"]),
desc=str(row["desc"]),
grade=int(row["grade"])
realm=Realm.from_id(int(row["stage_id"])),
item_ids=item_ids_list
)
plants_by_id[plant.id] = plant
plants_by_name[plant.name] = plant

View File

@@ -1,10 +1,13 @@
from dataclasses import dataclass, field
from typing import Union, TypeVar, Type
from typing import Union, TypeVar, Type, Optional
from enum import Enum
from abc import ABC, abstractmethod
from src.utils.df import game_configs
from src.utils.config import CONFIG
from src.classes.essence import EssenceType, Essence
from src.classes.animal import Animal, animals_by_id
from src.classes.plant import Plant, plants_by_id
def get_tiles_from_shape(shape: 'Shape', north_west_cor: str, south_east_cor: str) -> list[tuple[int, int]]:
@@ -182,13 +185,59 @@ class Shape(Enum):
class NormalRegion(Region):
"""
普通区域 - 平原、大河之类的,没有灵气或灵气很低
包含该区域分布的动植物物种信息
"""
animal_ids: list[int] = field(default_factory=list) # 该区域分布的动物物种IDs
plant_ids: list[int] = field(default_factory=list) # 该区域分布的植物物种IDs
# 这些字段将在__post_init__中设置
animals: list[Animal] = field(init=False, default_factory=list) # 该区域的动物实例
plants: list[Plant] = field(init=False, default_factory=list) # 该区域的植物实例
def __post_init__(self):
"""初始化动植物实例"""
# 先调用父类的__post_init__
super().__post_init__()
# 加载动物实例
for animal_id in self.animal_ids:
if animal_id in animals_by_id:
self.animals.append(animals_by_id[animal_id])
# 加载植物实例
for plant_id in self.plant_ids:
if plant_id in plants_by_id:
self.plants.append(plants_by_id[plant_id])
def get_region_type(self) -> str:
return "normal"
def get_species_info(self) -> str:
"""获取该区域动植物物种的描述信息"""
info_parts = []
if self.animals:
animal_infos = [animal.get_info() for animal in self.animals]
info_parts.extend(animal_infos)
if self.plants:
plant_infos = [plant.get_info() for plant in self.plants]
info_parts.extend(plant_infos)
return "; ".join(info_parts) if info_parts else "暂无特色物种"
def __str__(self) -> str:
return f"普通区域:{self.name} - {self.desc}"
species_info = self.get_species_info()
return f"普通区域:{self.name} - {self.desc} | 物种分布:{species_info}"
@property
def is_huntable(self) -> bool:
# 如果该区域有动物,则可以狩猎
return len(self.animals) > 0
@property
def is_harvestable(self) -> bool:
# 如果该区域有植物,则可以采集
return len(self.plants) > 0
@dataclass
@@ -262,6 +311,24 @@ def _load_regions(region_type: Type[T], config_name: str) -> tuple[dict[int, T],
base_params["essence_type"] = EssenceType.from_str(str(row["root_type"]))
base_params["essence_density"] = int(row["root_density"])
# 如果是普通区域添加动植物ID参数
elif region_type == NormalRegion:
# 处理动物IDs
animal_ids_list = []
animal_ids = row.get("animal_ids")
if animal_ids is not None and str(animal_ids).strip() and str(animal_ids) != 'nan':
for animal_id_str in str(animal_ids).split(CONFIG.df.ids_separator):
animal_ids_list.append(int(float(animal_id_str.strip())))
base_params["animal_ids"] = animal_ids_list
# 处理植物IDs
plant_ids_list = []
plant_ids = row.get("plant_ids")
if plant_ids is not None and str(plant_ids).strip() and str(plant_ids) != 'nan':
for plant_id_str in str(plant_ids).split(CONFIG.df.ids_separator):
plant_ids_list.append(int(float(plant_id_str.strip())))
base_params["plant_ids"] = plant_ids_list
region = region_type(**base_params)
regions_by_id[region.id] = region
regions_by_name[region.name] = region