This commit is contained in:
bridge
2026-01-06 23:01:25 +08:00
parent db6df82ea8
commit 8a23dc5576
19 changed files with 222 additions and 278 deletions

View File

@@ -38,6 +38,7 @@ from .assassinate import Assassinate
from .move_to_direction import MoveToDirection
from .cast import Cast
from .buy import Buy
from .mine import Mine
# 注册到 ActionRegistry标注是否为实际可执行动作
register_action(actual=False)(Action)
@@ -72,6 +73,7 @@ register_action(actual=True)(Assassinate)
register_action(actual=True)(MoveToDirection)
register_action(actual=True)(Cast)
register_action(actual=True)(Buy)
register_action(actual=True)(Mine)
# Talk 已移动到 mutual_action 模块,在那里注册
__all__ = [
@@ -109,8 +111,7 @@ __all__ = [
"MoveToDirection",
"Cast",
"Buy",
# Talk 已移动到 mutual_action 模块
# Occupy 已移动到 mutual_action 模块
"Mine",
]

View File

@@ -7,6 +7,7 @@ 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.lode import ORE_ITEM_IDS
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
@@ -32,7 +33,7 @@ class Cast(TimedAction):
Realm.Nascent_Soul: 0.1,
}
DOABLES_REQUIREMENTS = f"拥有{COST}个同境界材料"
DOABLES_REQUIREMENTS = f"拥有{COST}个同境界矿石材料"
PARAMS = {"target_realm": "目标境界名称('练气''筑基''金丹''元婴'"}
IS_MAJOR = False
@@ -48,13 +49,12 @@ class Cast(TimedAction):
def _count_materials(self, realm: Realm) -> int:
"""
统计符合条件的材料数量。
注意:仅统计 Item 类的直接实例,不统计 Weapon/Auxiliary 等子类(它们也是 Item但通常不作为铸造原材料
注意:仅统计 Item 类的直接实例,且必须在 ORE_ITEM_IDS 中
"""
count = 0
for item, qty in self.avatar.items.items():
# 这里使用 type(item) is Item 来严格限制必须是基础材料
# 如果项目里有其他继承自 Item 的材料类,可能需要放宽这个限制
if type(item).__name__ == "Item" and item.realm == realm:
# 增加判断item.id 必须在 ORE_ITEM_IDS 中
if type(item).__name__ == "Item" and item.realm == realm and item.id in ORE_ITEM_IDS:
count += qty
return count
@@ -80,7 +80,6 @@ class Cast(TimedAction):
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()
@@ -92,7 +91,7 @@ class Cast(TimedAction):
for item, qty in self.avatar.items.items():
if to_deduct <= 0:
break
if type(item).__name__ == "Item" and item.realm == self.target_realm:
if type(item).__name__ == "Item" and item.realm == self.target_realm and item.id in ORE_ITEM_IDS:
take = min(qty, to_deduct)
items_to_modify.append((item, take))
to_deduct -= take

View File

@@ -0,0 +1,72 @@
from __future__ import annotations
import random
from src.classes.action import TimedAction
from src.classes.event import Event
from src.classes.region import NormalRegion
class Mine(TimedAction):
"""
挖矿动作在有矿脉的区域进行挖矿持续6个月
可以获得矿脉对应的矿石
"""
ACTION_NAME = "挖矿"
EMOJI = "⛏️"
DESC = "在当前区域挖掘矿脉,获取矿石材料"
DOABLES_REQUIREMENTS = "在有矿脉的普通区域且avatar的境界必须大于等于矿脉的境界"
PARAMS = {}
duration_months = 6
def _execute(self) -> None:
"""
执行挖矿动作
"""
region = self.avatar.tile.region
lodes = getattr(region, "lodes", [])
if len(lodes) == 0:
return
available_lodes = [
lode for lode in lodes
if self.avatar.cultivation_progress.realm >= lode.realm
]
if len(available_lodes) == 0:
return
# 目前固定100%成功率
if random.random() < 1.0:
target_lode = random.choice(available_lodes)
# 随机选择该矿脉的一种物品
item = random.choice(target_lode.items)
# 基础获得1个额外物品来自effects
base_quantity = 1
extra_items = int(self.avatar.effects.get("extra_mine_items", 0) or 0)
total_quantity = base_quantity + extra_items
self.avatar.add_item(item, total_quantity)
def can_start(self) -> tuple[bool, str]:
region = self.avatar.tile.region
if not isinstance(region, NormalRegion):
return False, "当前不在普通区域"
lodes = getattr(region, "lodes", [])
if len(lodes) == 0:
return False, "当前区域没有矿脉"
available_lodes = [
lode for lode in lodes
if self.avatar.cultivation_progress.realm >= lode.realm
]
if len(available_lodes) == 0:
return False, "当前区域的矿脉境界过高"
return True, ""
def start(self) -> Event:
region = self.avatar.tile.region
return Event(self.world.month_stamp, f"{self.avatar.name}{self.avatar.tile.location_name} 开始挖矿", related_avatars=[self.avatar.id])
# TimedAction 已统一 step 逻辑
async def finish(self) -> list[Event]:
return []

View File

@@ -12,7 +12,6 @@ from src.classes.avatar.core import (
from src.classes.avatar.info_presenter import (
get_avatar_info,
get_avatar_structured_info,
get_avatar_hover_info,
get_avatar_expanded_info,
get_other_avatar_info,
)

View File

@@ -289,10 +289,6 @@ class Avatar(
from src.classes.avatar.info_presenter import get_avatar_structured_info
return get_avatar_structured_info(self)
def get_hover_info(self) -> list[str]:
from src.classes.avatar.info_presenter import get_avatar_hover_info
return get_avatar_hover_info(self)
def get_expanded_info(
self,
co_region_avatars: Optional[List["Avatar"]] = None,

View File

@@ -228,90 +228,6 @@ def get_avatar_structured_info(avatar: "Avatar") -> dict:
return info
def get_avatar_hover_info(avatar: "Avatar") -> list[str]:
"""
返回用于前端悬浮提示的多行信息。
"""
def add_kv(lines: list[str], key: str, value: object) -> None:
lines.append(f"{key}: {value}")
def add_section(lines: list[str], title: str, body: list[str]) -> None:
lines.append("")
lines.append(f"{title}:")
lines.extend(body)
lines: list[str] = []
# 基础信息
if avatar.nickname:
add_kv(lines, "绰号", f"{avatar.nickname.value}")
add_kv(lines, "性别", avatar.gender)
add_kv(lines, "年龄", avatar.age)
add_kv(lines, "外貌", avatar.appearance.get_info())
add_kv(lines, "阵营", avatar.alignment)
add_kv(lines, "境界", str(avatar.cultivation_progress))
add_kv(lines, "HP", avatar.hp)
add_kv(lines, "战斗力", int(get_base_strength(avatar)))
add_kv(lines, "宗门", avatar.get_sect_str())
from src.classes.root import format_root_cn
add_kv(lines, "灵根", format_root_cn(avatar.root))
tech_str = avatar.technique.get_colored_info() if avatar.technique is not None else ""
add_kv(lines, "功法", tech_str)
if avatar.personas:
persona_parts = [p.get_colored_info() for p in avatar.personas]
add_kv(lines, "特质", ", ".join(persona_parts))
add_kv(lines, "灵石", str(avatar.magic_stone))
# 物品
if avatar.items:
items_lines = [f" {item.name} x{quantity}" for item, quantity in avatar.items.items()]
add_section(lines, "物品", items_lines)
else:
add_kv(lines, "物品", "")
# 思考与目标
if avatar.thinking:
add_section(lines, "正在思考", [avatar.thinking])
if avatar.long_term_objective:
add_section(lines, "长期目标", [avatar.long_term_objective.content])
if avatar.short_term_objective:
add_section(lines, "短期目标", [avatar.short_term_objective])
# 兵器(必有,使用颜色标记等级)
if avatar.weapon is not None:
weapon_text = avatar.weapon.get_colored_info()
if avatar.weapon.desc:
weapon_text += f"{avatar.weapon.desc}"
add_kv(lines, "兵器", weapon_text)
# 辅助装备(可选,使用颜色标记等级)
if avatar.auxiliary is not None:
auxiliary_text = avatar.auxiliary.get_colored_info()
if avatar.auxiliary.desc:
auxiliary_text += f"{avatar.auxiliary.desc}"
add_kv(lines, "辅助装备", auxiliary_text)
else:
add_kv(lines, "辅助装备", "")
# 灵兽:仅在存在时显示
if avatar.spirit_animal is not None:
add_kv(lines, "灵兽", avatar.spirit_animal.get_info())
# 关系(从自身视角分组展示)
from src.classes.relation import get_relations_strs
relation_lines = get_relations_strs(avatar, max_lines=15)
if relation_lines:
add_section(lines, "关系", [f" {s}" for s in relation_lines])
else:
add_kv(lines, "关系", "")
return lines
def get_avatar_expanded_info(
avatar: "Avatar",
co_region_avatars: Optional[List["Avatar"]] = None,

View File

@@ -115,14 +115,6 @@ def split_colored_segments(text: str) -> list[dict[str, str]]:
return segments
def serialize_hover_lines(lines: list[str]) -> list[list[dict[str, str]]]:
"""将 hover 信息行转换为 segment 列表,供前端直接渲染颜色。"""
serialized: list[list[dict[str, str]]] = []
for line in lines:
serialized.append(split_colored_segments(line or ""))
return serialized
# ==================== 颜色方案映射 ====================
# 装备等级颜色方案(普通-宝物-法宝)

87
src/classes/lode.py Normal file
View File

@@ -0,0 +1,87 @@
from dataclasses import dataclass, field
from typing import Optional
from src.utils.df import game_configs, get_str, get_int, get_list_int
from src.classes.item import Item, items_by_id
from src.classes.cultivation import Realm
@dataclass
class Lode:
"""
矿脉
"""
id: int
name: str
desc: str
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 get_structured_info(self) -> dict:
items_info = [item.get_structured_info() for item in self.items]
return {
"id": str(self.id),
"name": self.name,
"desc": self.desc,
"grade": self.realm.value,
"drops": items_info,
"type": "lode"
}
def _load_lodes() -> tuple[dict[int, Lode], dict[str, Lode]]:
"""从配表加载lode数据"""
lodes_by_id: dict[int, Lode] = {}
lodes_by_name: dict[str, Lode] = {}
# 检查配置是否存在,避免初始化错误
if "lode" not in game_configs:
return {}, {}
lode_df = game_configs["lode"]
for row in lode_df:
item_ids_list = get_list_int(row, "item_ids")
lode = Lode(
id=get_int(row, "id"),
name=get_str(row, "name"),
desc=get_str(row, "desc"),
realm=Realm.from_id(get_int(row, "stage_id")),
item_ids=item_ids_list
)
lodes_by_id[lode.id] = lode
lodes_by_name[lode.name] = lode
return lodes_by_id, lodes_by_name
# 从配表加载lode数据
lodes_by_id, lodes_by_name = _load_lodes()
# 导出所有属于矿石的物品ID供铸造逻辑判断
ORE_ITEM_IDS = {item_id for lode in lodes_by_id.values() for item_id in lode.item_ids}

View File

@@ -111,7 +111,7 @@ class Map():
return {
"修炼区域(可以修炼以增进修为)": build_regions_info(filter_regions(CultivateRegion)),
"普通区域(可以狩猎采集)": build_regions_info(filter_regions(NormalRegion)),
"普通区域(可以狩猎采集、挖矿": build_regions_info(filter_regions(NormalRegion)),
"城市区域(可以交易)": build_regions_info(filter_regions(CityRegion)),
"宗门总部(宗门弟子可在此进行疗伤等操作)": build_regions_info(filter_regions(SectRegion)),
}

View File

@@ -9,6 +9,7 @@ from src.utils.distance import chebyshev_distance
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
from src.classes.lode import Lode, lodes_by_id
from src.classes.sect import sects_by_name
if TYPE_CHECKING:
@@ -64,12 +65,6 @@ class Region(ABC):
def get_region_type(self) -> str:
pass
def get_hover_info(self) -> list[str]:
return [
f"区域: {self.name}",
f"描述: {self.desc}",
]
@abstractmethod
def _get_desc(self) -> str:
"""
@@ -109,9 +104,11 @@ class NormalRegion(Region):
"""普通区域"""
animal_ids: list[int] = field(default_factory=list)
plant_ids: list[int] = field(default_factory=list)
lode_ids: list[int] = field(default_factory=list)
animals: list[Animal] = field(init=False, default_factory=list)
plants: list[Plant] = field(init=False, default_factory=list)
lodes: list[Lode] = field(init=False, default_factory=list)
def __post_init__(self):
super().__post_init__()
@@ -121,6 +118,9 @@ class NormalRegion(Region):
for plant_id in self.plant_ids:
if plant_id in plants_by_id:
self.plants.append(plants_by_id[plant_id])
for lode_id in self.lode_ids:
if lode_id in lodes_by_id:
self.lodes.append(lodes_by_id[lode_id])
def get_region_type(self) -> str:
return "normal"
@@ -131,26 +131,17 @@ class NormalRegion(Region):
info_parts.extend([a.get_info() for a in self.animals])
if self.plants:
info_parts.extend([p.get_info() for p in self.plants])
return "; ".join(info_parts) if info_parts else "无特色物种"
if self.lodes:
info_parts.extend([l.get_info() for l in self.lodes])
return "; ".join(info_parts) if info_parts else "无特色资源"
def _get_desc(self) -> str:
species_info = self.get_species_info()
return f"物种分布:{species_info}"
return f"资源分布:{species_info}"
def __str__(self) -> str:
species_info = self.get_species_info()
return f"普通区域:{self.name} - {self.desc} | 物种分布:{species_info}"
def get_hover_info(self) -> list[str]:
lines = super().get_hover_info()
species_info = self.get_species_info()
if species_info and species_info != "暂无特色物种":
lines.append("物种分布:")
for species in species_info.split("; "):
lines.append(f" {species}")
else:
lines.append("物种分布: 暂无特色物种")
return lines
return f"普通区域:{self.name} - {self.desc} | 资源分布:{species_info}"
@property
def is_huntable(self) -> bool:
@@ -160,6 +151,10 @@ class NormalRegion(Region):
def is_harvestable(self) -> bool:
return len(self.plants) > 0
@property
def is_mineable(self) -> bool:
return len(self.lodes) > 0
def get_structured_info(self) -> dict:
info = super().get_structured_info()
info["type_name"] = "普通区域"
@@ -167,6 +162,7 @@ class NormalRegion(Region):
# Assuming animals and plants are populated in __post_init__
info["animals"] = [a.get_structured_info() for a in self.animals] if self.animals else []
info["plants"] = [p.get_structured_info() for p in self.plants] if self.plants else []
info["lodes"] = [l.get_structured_info() for l in self.lodes] if self.lodes else []
return info
@@ -196,16 +192,6 @@ class CultivateRegion(Region):
def __str__(self) -> str:
return f"修炼区域:{self.name}{self.essence_type}行灵气:{self.essence_density}- {self.desc}"
def get_hover_info(self) -> list[str]:
lines = super().get_hover_info()
stars = "" * self.essence_density + "" * (10 - self.essence_density)
lines.append(f"主要灵气: {self.essence_type} {stars}")
if self.host_avatar:
lines.append(f"主人: {self.host_avatar.name}")
else:
lines.append("主人: 无(可占据)")
return lines
def get_structured_info(self) -> dict:
info = super().get_structured_info()
info["type_name"] = "修炼区域"

View File

@@ -28,7 +28,6 @@ from src.classes.appearance import get_appearance_by_level
from src.classes.persona import personas_by_id
from src.classes.cultivation import REALM_ORDER
from src.classes.alignment import Alignment
from src.classes.color import serialize_hover_lines
from src.classes.event import Event
from src.classes.celestial_phenomenon import celestial_phenomena_by_id
from src.classes.long_term_objective import set_user_long_term_objective, clear_user_long_term_objective
@@ -707,43 +706,6 @@ def resume_game():
game_instance["is_paused"] = False
return {"status": "ok", "message": "Game resumed"}
@app.get("/api/hover")
def get_hover_info(
target_type: str = Query(alias="type"),
target_id: str = Query(alias="id")
):
world = game_instance.get("world")
if world is None:
raise HTTPException(status_code=503, detail="World not initialized")
target = None
if target_type == "avatar":
target = world.avatar_manager.get_avatar(target_id)
elif target_type == "region":
if world.map and hasattr(world.map, "regions"):
regions = world.map.regions
target = regions.get(target_id)
if target is None:
try:
target = regions.get(int(target_id))
except (ValueError, TypeError):
target = None
else:
raise HTTPException(status_code=400, detail="Unsupported target type")
if target is None:
raise HTTPException(status_code=404, detail="Target not found")
if not hasattr(target, "get_hover_info"):
raise HTTPException(status_code=422, detail="Target has no hover info")
lines = target.get_hover_info() or []
return {
"id": target_id,
"type": target_type,
"name": getattr(target, "name", target_id),
"lines": serialize_hover_lines([str(line) for line in lines]),
}
@app.get("/api/detail")
def get_detail_info(
target_type: str = Query(alias="type"),
@@ -777,19 +739,8 @@ def get_detail_info(
if target is None:
raise HTTPException(status_code=404, detail="Target not found")
if hasattr(target, "get_structured_info"):
info = target.get_structured_info()
return info
else:
# 回退到 hover info 如果没有结构化信息
if hasattr(target, "get_hover_info"):
lines = target.get_hover_info() or []
return {
"fallback": True,
"name": getattr(target, "name", target_id),
"lines": serialize_hover_lines([str(line) for line in lines])
}
return {"error": "No info available"}
info = target.get_structured_info()
return info
class SetObjectiveRequest(BaseModel):
avatar_id: str

View File

@@ -21,4 +21,8 @@ id,name,desc,stage_id
19,清音竹节,九曲清音竹中最精华的一节,敲击时能发出安神定志的清音,是制作音波类法宝的绝佳材料,2
20,冰魄蛇胆,冰魄骨蛇的内丹所在,虽为极寒之物,但吞服后能大幅增强修士对寒气的亲和力,是修炼寒系功法的至宝,2
21,星纹叶,叶面上天生带有星辰纹路的草叶,蕴含纯净的星辰之力,是炼制星辰属性丹药的主材,2
22,赤果核,涅盘赤果的果核,即便果肉枯萎,核心仍锁有一丝不灭生机,是炼制延寿丹和疗伤圣药的关键,3
22,赤果核,涅盘赤果的果核,即便果肉枯萎,核心仍锁有一丝不灭生机,是炼制延寿丹和疗伤圣药的关键,3
101,玄铁,普通的黑色铁矿,质地坚硬,适合打造练气期法器,1
102,赤精铜,通体赤红的铜矿,蕴含火灵之力,是筑基期炼器的常用材料,2
103,秘银,银白色的稀有金属,延展性极佳且能传导灵力,适用于金丹期法宝,3
104,庚金,锐气逼人的金色矿石,坚不可摧,乃是元婴期炼制本命法宝的顶级材料,4
1 id name desc stage_id
21 19 清音竹节 九曲清音竹中最精华的一节,敲击时能发出安神定志的清音,是制作音波类法宝的绝佳材料 2
22 20 冰魄蛇胆 冰魄骨蛇的内丹所在,虽为极寒之物,但吞服后能大幅增强修士对寒气的亲和力,是修炼寒系功法的至宝 2
23 21 星纹叶 叶面上天生带有星辰纹路的草叶,蕴含纯净的星辰之力,是炼制星辰属性丹药的主材 2
24 22 赤果核 涅盘赤果的果核,即便果肉枯萎,核心仍锁有一丝不灭生机,是炼制延寿丹和疗伤圣药的关键 3
25 101 玄铁 普通的黑色铁矿,质地坚硬,适合打造练气期法器 1
26 102 赤精铜 通体赤红的铜矿,蕴含火灵之力,是筑基期炼器的常用材料 2
27 103 秘银 银白色的稀有金属,延展性极佳且能传导灵力,适用于金丹期法宝 3
28 104 庚金 锐气逼人的金色矿石,坚不可摧,乃是元婴期炼制本命法宝的顶级材料 4

View File

@@ -0,0 +1,7 @@
id,name,desc,stage_id,item_ids
,,,"该矿脉对应的物品ID"
1,玄铁矿脉,蕴含玄铁的普通矿脉,常见于浅层地表,1,101
2,赤铜矿脉,深埋地下的赤铜矿脉,周围往往伴生火热之气,2,102
3,秘银矿床,极其罕见的秘银富集地,银光闪烁,灵气逼人,3,103
4,庚金矿洞,位于绝壁深处的庚金矿脉,开采极为困难,但价值连城,4,104
1 id name desc stage_id item_ids
2 该矿脉对应的物品ID
3 1 玄铁矿脉 蕴含玄铁的普通矿脉,常见于浅层地表 1 101
4 2 赤铜矿脉 深埋地下的赤铜矿脉,周围往往伴生火热之气 2 102
5 3 秘银矿床 极其罕见的秘银富集地,银光闪烁,灵气逼人 3 103
6 4 庚金矿洞 位于绝壁深处的庚金矿脉,开采极为困难,但价值连城 4 104

View File

@@ -1,20 +1,20 @@
id,name,desc,animal_ids,plant_ids
ID必须以1开头,,,该区域分布的动物物种IDs,该区域分布的植物物种IDs
101,平原地带,位于大陆中部,地势平坦,灵气平和。是初学修炼者打基础和建立宗门的理想之地。,3.0,
102,西域流沙,位于大陆极西,茫茫大漠,黄沙漫天。此地气候干燥,日夜温差极大,是沙漠商队的必经之路。,,3.0
103,南疆蛮荒,位于大陆西南,古木参天,藤蔓缠绕。此地森林茂密,野兽众多,是采集药材和狩猎的危险之地。,4.0,
104,极北冰原,位于大陆极北,千里冰封,万年不化。此地严寒刺骨,风雪交加,只有最坚韧的冒险者才能在此生存。,,4.0
105,无边碧海,位于大陆极东,浩瀚无垠,波涛汹涌。此地风浪险恶,暗礁密布,是海商和渔民的挑战之海。,5.0,
106,天河奔流,横贯大陆东西,一江春水向东流,奔腾不息入东海。此河贯穿东西,水流湍急,是重要的交通要道。,,5.0
107,青峰山脉,位于大陆东部,连绵起伏,直插云霄。此地山势险峻,多有奇石异洞,是探险者寻宝的热门之地。,6.0,
108,万丈雪峰,位于大陆西北,雪峰皑皑,寒风刺骨。此地终年积雪,山路崎岖,是登山者的终极挑战。,,6.0
109,碧野千里,位于大陆西部,芳草萋萋,一望无际。此地水草丰美,牛羊成群,是游牧民族的天然牧场。,7.0,
110,青云林海,位于大陆中部,古树参天,绿意盎然。此地森林广袤,物产丰富,是伐木工和猎人的主要活动区域。,,7.0
111,炎狱火山,位于大陆东北,烈焰冲天,岩浆奔流。此地火山活跃,地热丰富,是铁匠锻造的理想之地,但也极其危险。,8.0,
112,沃土良田,位于大陆东部,土地肥沃,五谷丰登。此地土壤深厚,雨水充沛,是农民耕种的黄金宝地。,,8.0
113,幽冥毒泽,位于大陆正南,终年被五色瘴气笼罩,毒虫遍地。凡人入之即化为白骨,唯有修习毒功者视此处为无上洞天。,9.0,
114,十万大山,位于大陆南部,苍茫群山连绵不绝,乃是妖族祖地。山势险峻,道路难行。,10.0,
115,紫竹幽境,位于大陆中北,紫竹成林,灵气清冽。风过林间若奏仙乐,在此静修可涤荡心魔,感悟天地自然之道。,,9.0
116,凛霜荒原,位于大陆东北,寸草不生,冻土千尺。此地生机绝灭,却蕴含着极致的阴寒灵气,偶有万年玄冰出世。,11.0,
117,碎星戈壁,位于大陆西南,飞沙走石,狂风如刀。传说曾有星辰陨落于此,至今仍残存着狂暴的星辰之力与天外陨铁。,,10.0
118,蓬莱遗岛,位于极东海外,孤悬海外,云雾缭绕。岛上灵泉喷涌,奇花异草遍布,灵气浓郁远超内陆,是海外散修向往之地。,,11.0
id,name,desc,animal_ids,plant_ids,lode_ids
ID必须以1开头,,,该区域分布的动物物种IDs,该区域分布的植物物种IDs,该区域分布的矿脉IDs
101,平原地带,位于大陆中部,地势平坦,灵气平和。是初学修炼者打基础和建立宗门的理想之地。,3.0,,
102,西域流沙,位于大陆极西,茫茫大漠,黄沙漫天。此地气候干燥,日夜温差极大,是沙漠商队的必经之路。,,3.0,
103,南疆蛮荒,位于大陆西南,古木参天,藤蔓缠绕。此地森林茂密,野兽众多,是采集药材和狩猎的危险之地。,4.0,,
104,极北冰原,位于大陆极北,千里冰封,万年不化。此地严寒刺骨,风雪交加,只有最坚韧的冒险者才能在此生存。,,4.0,
105,无边碧海,位于大陆极东,浩瀚无垠,波涛汹涌。此地风浪险恶,暗礁密布,是海商和渔民的挑战之海。,5.0,,
106,天河奔流,横贯大陆东西,一江春水向东流,奔腾不息入东海。此河贯穿东西,水流湍急,是重要的交通要道。,,5.0,
107,青峰山脉,位于大陆东部,连绵起伏,直插云霄。此地山势险峻,多有奇石异洞,是探险者寻宝的热门之地。,6.0,,1.0
108,万丈雪峰,位于大陆西北,雪峰皑皑,寒风刺骨。此地终年积雪,山路崎岖,是登山者的终极挑战。,,6.0,
109,碧野千里,位于大陆西部,芳草萋萋,一望无际。此地水草丰美,牛羊成群,是游牧民族的天然牧场。,7.0,,
110,青云林海,位于大陆中部,古树参天,绿意盎然。此地森林广袤,物产丰富,是伐木工和猎人的主要活动区域。,,7.0,
111,炎狱火山,位于大陆东北,烈焰冲天,岩浆奔流。此地火山活跃,地热丰富,是铁匠锻造的理想之地,但也极其危险。,8.0,,2.0
112,沃土良田,位于大陆东部,土地肥沃,五谷丰登。此地土壤深厚,雨水充沛,是农民耕种的黄金宝地。,,8.0,
113,幽冥毒泽,位于大陆正南,终年被五色瘴气笼罩,毒虫遍地。凡人入之即化为白骨,唯有修习毒功者视此处为无上洞天。,9.0,,
114,十万大山,位于大陆南部,苍茫群山连绵不绝,乃是妖族祖地。山势险峻,道路难行。,10.0,,4.0
115,紫竹幽境,位于大陆中北,紫竹成林,灵气清冽。风过林间若奏仙乐,在此静修可涤荡心魔,感悟天地自然之道。,,9.0,
116,凛霜荒原,位于大陆东北,寸草不生,冻土千尺。此地生机绝灭,却蕴含着极致的阴寒灵气,偶有万年玄冰出世。,11.0,,
117,碎星戈壁,位于大陆西南,飞沙走石,狂风如刀。传说曾有星辰陨落于此,至今仍残存着狂暴的星辰之力与天外陨铁。,,10.0,3.0
118,蓬莱遗岛,位于极东海外,孤悬海外,云雾缭绕。岛上灵泉喷涌,奇花异草遍布,灵气浓郁远超内陆,是海外散修向往之地。,,11.0,
1 id name desc animal_ids plant_ids lode_ids
2 ID必须以1开头 该区域分布的动物物种IDs 该区域分布的植物物种IDs 该区域分布的矿脉IDs
3 101 平原地带 位于大陆中部,地势平坦,灵气平和。是初学修炼者打基础和建立宗门的理想之地。 3.0
4 102 西域流沙 位于大陆极西,茫茫大漠,黄沙漫天。此地气候干燥,日夜温差极大,是沙漠商队的必经之路。 3.0
5 103 南疆蛮荒 位于大陆西南,古木参天,藤蔓缠绕。此地森林茂密,野兽众多,是采集药材和狩猎的危险之地。 4.0
6 104 极北冰原 位于大陆极北,千里冰封,万年不化。此地严寒刺骨,风雪交加,只有最坚韧的冒险者才能在此生存。 4.0
7 105 无边碧海 位于大陆极东,浩瀚无垠,波涛汹涌。此地风浪险恶,暗礁密布,是海商和渔民的挑战之海。 5.0
8 106 天河奔流 横贯大陆东西,一江春水向东流,奔腾不息入东海。此河贯穿东西,水流湍急,是重要的交通要道。 5.0
9 107 青峰山脉 位于大陆东部,连绵起伏,直插云霄。此地山势险峻,多有奇石异洞,是探险者寻宝的热门之地。 6.0 1.0
10 108 万丈雪峰 位于大陆西北,雪峰皑皑,寒风刺骨。此地终年积雪,山路崎岖,是登山者的终极挑战。 6.0
11 109 碧野千里 位于大陆西部,芳草萋萋,一望无际。此地水草丰美,牛羊成群,是游牧民族的天然牧场。 7.0
12 110 青云林海 位于大陆中部,古树参天,绿意盎然。此地森林广袤,物产丰富,是伐木工和猎人的主要活动区域。 7.0
13 111 炎狱火山 位于大陆东北,烈焰冲天,岩浆奔流。此地火山活跃,地热丰富,是铁匠锻造的理想之地,但也极其危险。 8.0 2.0
14 112 沃土良田 位于大陆东部,土地肥沃,五谷丰登。此地土壤深厚,雨水充沛,是农民耕种的黄金宝地。 8.0
15 113 幽冥毒泽 位于大陆正南,终年被五色瘴气笼罩,毒虫遍地。凡人入之即化为白骨,唯有修习毒功者视此处为无上洞天。 9.0
16 114 十万大山 位于大陆南部,苍茫群山连绵不绝,乃是妖族祖地。山势险峻,道路难行。 10.0 4.0
17 115 紫竹幽境 位于大陆中北,紫竹成林,灵气清冽。风过林间若奏仙乐,在此静修可涤荡心魔,感悟天地自然之道。 9.0
18 116 凛霜荒原 位于大陆东北,寸草不生,冻土千尺。此地生机绝灭,却蕴含着极致的阴寒灵气,偶有万年玄冰出世。 11.0
19 117 碎星戈壁 位于大陆西南,飞沙走石,狂风如刀。传说曾有星辰陨落于此,至今仍残存着狂暴的星辰之力与天外陨铁。 10.0 3.0
20 118 蓬莱遗岛 位于极东海外,孤悬海外,云雾缭绕。岛上灵泉喷涌,奇花异草遍布,灵气浓郁远超内陆,是海外散修向往之地。 11.0

View File

@@ -62,3 +62,4 @@ id,name,exclusion_names,desc,rarity,condition,effects
60,大器晚成,,早年修行多舛,霉运连连;但若能坚持至金丹元婴,便可否极泰来,气运亨通。,SR,,"[{when: 'avatar.cultivation_progress.realm.value in [""练气"", ""筑基""]', extra_misfortune_probability: 0.005}, {when: 'avatar.cultivation_progress.realm.value in [""金丹"", ""元婴""]', extra_fortune_probability: 0.01}]"
61,炼器师,好斗,精通炼器之道,对材料敏锐,擅长铸造法宝。你认为法宝是修行的关键,战斗并非你的专长。,R,,{extra_cast_success_rate: 0.15}
62,情绪化,理性;淡漠,你的情绪波动很大,极易受外界事件影响而改变心情,做事也更随心所欲。,N,,
63,矿工,怠惰,擅长勘探挖掘,对矿脉有着独特的直觉。你认为地下的宝藏才是最实在的财富。,R,,{extra_mine_items: 1}
1 id name exclusion_names desc rarity condition effects
62 60 大器晚成 早年修行多舛,霉运连连;但若能坚持至金丹元婴,便可否极泰来,气运亨通。 SR [{when: 'avatar.cultivation_progress.realm.value in ["练气", "筑基"]', extra_misfortune_probability: 0.005}, {when: 'avatar.cultivation_progress.realm.value in ["金丹", "元婴"]', extra_fortune_probability: 0.01}]
63 61 炼器师 好斗 精通炼器之道,对材料敏锐,擅长铸造法宝。你认为法宝是修行的关键,战斗并非你的专长。 R {extra_cast_success_rate: 0.15}
64 62 情绪化 理性;淡漠 你的情绪波动很大,极易受外界事件影响而改变心情,做事也更随心所欲。 N
65 63 矿工 怠惰 擅长勘探挖掘,对矿脉有着独特的直觉。你认为地下的宝藏才是最实在的财富。 R {extra_mine_items: 1}

View File

@@ -2,7 +2,6 @@ import { httpClient } from './http';
import type {
InitialStateDTO,
MapResponseDTO,
HoverResponseDTO,
DetailResponseDTO,
SaveFileDTO
} from '../types/api';
@@ -92,11 +91,6 @@ export const gameApi = {
// --- Information ---
fetchHoverInfo(params: HoverParams) {
const query = new URLSearchParams(Object.entries(params));
return httpClient.get<HoverResponseDTO>(`/api/hover?${query}`);
},
fetchDetailInfo(params: HoverParams) {
const query = new URLSearchParams(Object.entries(params));
return httpClient.get<DetailResponseDTO>(`/api/detail?${query}`);

View File

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { gameApi } from '../api/game';
import type { AvatarDetail, RegionDetail, SectDetail, HoverLine } from '../types/core';
import type { AvatarDetail, RegionDetail, SectDetail } from '../types/core';
export type SelectionType = 'avatar' | 'region' | 'sect';
@@ -20,11 +20,6 @@ export const useUiStore = defineStore('ui', () => {
const isLoadingDetail = ref(false);
const detailError = ref<string | null>(null);
// --- Hover ---
const hoveringTarget = ref<Selection | null>(null);
const hoverInfo = ref<HoverLine[]>([]);
// --- Actions ---
async function select(type: SelectionType, id: string) {
@@ -78,58 +73,15 @@ export const useUiStore = defineStore('ui', () => {
}
}
// --- Hover Actions ---
// Simple cache for hover
const hoverCache = new Map<string, HoverLine[]>();
async function setHover(type: SelectionType | null, id?: string) {
if (!type || !id) {
hoveringTarget.value = null;
return;
}
const key = `${type}:${id}`;
hoveringTarget.value = { type, id };
// Check cache
if (hoverCache.has(key)) {
hoverInfo.value = hoverCache.get(key)!;
return;
}
try {
const res = await gameApi.fetchHoverInfo({ type, id });
// Normalize lines... (Assuming backend returns lines compatible with HoverLine)
const lines = res.lines as HoverLine[];
hoverCache.set(key, lines);
if (hoveringTarget.value?.type === type && hoveringTarget.value?.id === id) {
hoverInfo.value = lines;
}
} catch (e) {
console.warn('Hover fetch failed', e);
}
}
function clearHoverCache() {
hoverCache.clear();
}
return {
selectedTarget,
detailData,
isLoadingDetail,
detailError,
hoveringTarget,
hoverInfo,
select,
clearSelection,
refreshDetail,
setHover,
clearHoverCache
refreshDetail
};
});

View File

@@ -57,10 +57,6 @@ export interface MapResponseDTO {
};
}
export interface HoverResponseDTO {
lines: unknown; // 后端返回的可能是复杂的嵌套数组
}
// 详情接口返回的结构比较动态,通常包含 entity 的所有字段
export type DetailResponseDTO = Record<string, any>;

View File

@@ -194,12 +194,3 @@ export interface GameEvent {
// 运行时辅助字段
_seq?: number;
}
// --- 悬浮提示 (Hover) ---
export type HoverSegment = {
text: string;
color?: string;
};
export type HoverLine = HoverSegment[];