From 8a23dc55761c7bed578e3525868f63c7873e7508 Mon Sep 17 00:00:00 2001 From: bridge Date: Tue, 6 Jan 2026 23:01:25 +0800 Subject: [PATCH] add lode --- src/classes/action/__init__.py | 5 +- src/classes/action/cast.py | 13 ++-- src/classes/action/mine.py | 72 ++++++++++++++++++++++ src/classes/avatar/__init__.py | 1 - src/classes/avatar/core.py | 4 -- src/classes/avatar/info_presenter.py | 84 -------------------------- src/classes/color.py | 8 --- src/classes/lode.py | 87 +++++++++++++++++++++++++++ src/classes/map.py | 2 +- src/classes/region.py | 46 +++++--------- src/server/main.py | 53 +--------------- static/game_configs/item.csv | 6 +- static/game_configs/lode.csv | 7 +++ static/game_configs/normal_region.csv | 40 ++++++------ static/game_configs/persona.csv | 1 + web/src/api/game.ts | 6 -- web/src/stores/ui.ts | 52 +--------------- web/src/types/api.ts | 4 -- web/src/types/core.ts | 9 --- 19 files changed, 222 insertions(+), 278 deletions(-) create mode 100644 src/classes/action/mine.py create mode 100644 src/classes/lode.py create mode 100644 static/game_configs/lode.csv diff --git a/src/classes/action/__init__.py b/src/classes/action/__init__.py index e9c2e2c..d21dea0 100644 --- a/src/classes/action/__init__.py +++ b/src/classes/action/__init__.py @@ -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", ] diff --git a/src/classes/action/cast.py b/src/classes/action/cast.py index 3b07be8..38a285d 100644 --- a/src/classes/action/cast.py +++ b/src/classes/action/cast.py @@ -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 diff --git a/src/classes/action/mine.py b/src/classes/action/mine.py new file mode 100644 index 0000000..a5645c1 --- /dev/null +++ b/src/classes/action/mine.py @@ -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 [] + diff --git a/src/classes/avatar/__init__.py b/src/classes/avatar/__init__.py index 3a021d4..90f2914 100644 --- a/src/classes/avatar/__init__.py +++ b/src/classes/avatar/__init__.py @@ -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, ) diff --git a/src/classes/avatar/core.py b/src/classes/avatar/core.py index 0bbae49..faa194c 100644 --- a/src/classes/avatar/core.py +++ b/src/classes/avatar/core.py @@ -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, diff --git a/src/classes/avatar/info_presenter.py b/src/classes/avatar/info_presenter.py index ab3957e..dd802dc 100644 --- a/src/classes/avatar/info_presenter.py +++ b/src/classes/avatar/info_presenter.py @@ -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, diff --git a/src/classes/color.py b/src/classes/color.py index 28eaf73..886618b 100644 --- a/src/classes/color.py +++ b/src/classes/color.py @@ -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 - - # ==================== 颜色方案映射 ==================== # 装备等级颜色方案(普通-宝物-法宝) diff --git a/src/classes/lode.py b/src/classes/lode.py new file mode 100644 index 0000000..431f7f7 --- /dev/null +++ b/src/classes/lode.py @@ -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} + diff --git a/src/classes/map.py b/src/classes/map.py index 75ce62b..37cb99d 100644 --- a/src/classes/map.py +++ b/src/classes/map.py @@ -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)), } diff --git a/src/classes/region.py b/src/classes/region.py index dcceb86..ec56287 100644 --- a/src/classes/region.py +++ b/src/classes/region.py @@ -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"] = "修炼区域" diff --git a/src/server/main.py b/src/server/main.py index 2abf16e..7649283 100644 --- a/src/server/main.py +++ b/src/server/main.py @@ -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 diff --git a/static/game_configs/item.csv b/static/game_configs/item.csv index 41498bb..5f7ff1e 100644 --- a/static/game_configs/item.csv +++ b/static/game_configs/item.csv @@ -21,4 +21,8 @@ id,name,desc,stage_id 19,清音竹节,九曲清音竹中最精华的一节,敲击时能发出安神定志的清音,是制作音波类法宝的绝佳材料,2 20,冰魄蛇胆,冰魄骨蛇的内丹所在,虽为极寒之物,但吞服后能大幅增强修士对寒气的亲和力,是修炼寒系功法的至宝,2 21,星纹叶,叶面上天生带有星辰纹路的草叶,蕴含纯净的星辰之力,是炼制星辰属性丹药的主材,2 -22,赤果核,涅盘赤果的果核,即便果肉枯萎,核心仍锁有一丝不灭生机,是炼制延寿丹和疗伤圣药的关键,3 \ No newline at end of file +22,赤果核,涅盘赤果的果核,即便果肉枯萎,核心仍锁有一丝不灭生机,是炼制延寿丹和疗伤圣药的关键,3 +101,玄铁,普通的黑色铁矿,质地坚硬,适合打造练气期法器,1 +102,赤精铜,通体赤红的铜矿,蕴含火灵之力,是筑基期炼器的常用材料,2 +103,秘银,银白色的稀有金属,延展性极佳且能传导灵力,适用于金丹期法宝,3 +104,庚金,锐气逼人的金色矿石,坚不可摧,乃是元婴期炼制本命法宝的顶级材料,4 diff --git a/static/game_configs/lode.csv b/static/game_configs/lode.csv new file mode 100644 index 0000000..a5862ea --- /dev/null +++ b/static/game_configs/lode.csv @@ -0,0 +1,7 @@ +id,name,desc,stage_id,item_ids +,,,"该矿脉对应的物品ID" +1,玄铁矿脉,蕴含玄铁的普通矿脉,常见于浅层地表,1,101 +2,赤铜矿脉,深埋地下的赤铜矿脉,周围往往伴生火热之气,2,102 +3,秘银矿床,极其罕见的秘银富集地,银光闪烁,灵气逼人,3,103 +4,庚金矿洞,位于绝壁深处的庚金矿脉,开采极为困难,但价值连城,4,104 + diff --git a/static/game_configs/normal_region.csv b/static/game_configs/normal_region.csv index 49dfaab..d987884 100644 --- a/static/game_configs/normal_region.csv +++ b/static/game_configs/normal_region.csv @@ -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, diff --git a/static/game_configs/persona.csv b/static/game_configs/persona.csv index 6db5dd9..89d1b43 100644 --- a/static/game_configs/persona.csv +++ b/static/game_configs/persona.csv @@ -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} diff --git a/web/src/api/game.ts b/web/src/api/game.ts index cf36742..38a1e80 100644 --- a/web/src/api/game.ts +++ b/web/src/api/game.ts @@ -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(`/api/hover?${query}`); - }, - fetchDetailInfo(params: HoverParams) { const query = new URLSearchParams(Object.entries(params)); return httpClient.get(`/api/detail?${query}`); diff --git a/web/src/stores/ui.ts b/web/src/stores/ui.ts index 145ab3e..722328a 100644 --- a/web/src/stores/ui.ts +++ b/web/src/stores/ui.ts @@ -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(null); - // --- Hover --- - - const hoveringTarget = ref(null); - const hoverInfo = ref([]); - // --- 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(); - - 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 }; }); diff --git a/web/src/types/api.ts b/web/src/types/api.ts index 46720ed..654c7bf 100644 --- a/web/src/types/api.ts +++ b/web/src/types/api.ts @@ -57,10 +57,6 @@ export interface MapResponseDTO { }; } -export interface HoverResponseDTO { - lines: unknown; // 后端返回的可能是复杂的嵌套数组 -} - // 详情接口返回的结构比较动态,通常包含 entity 的所有字段 export type DetailResponseDTO = Record; diff --git a/web/src/types/core.ts b/web/src/types/core.ts index c141922..8a5bfb9 100644 --- a/web/src/types/core.ts +++ b/web/src/types/core.ts @@ -194,12 +194,3 @@ export interface GameEvent { // 运行时辅助字段 _seq?: number; } - -// --- 悬浮提示 (Hover) --- - -export type HoverSegment = { - text: string; - color?: string; -}; - -export type HoverLine = HoverSegment[];