diff --git a/src/server/main.py b/src/server/main.py index fcd3c53..e7356d9 100644 --- a/src/server/main.py +++ b/src/server/main.py @@ -694,6 +694,7 @@ class CreateAvatarRequest(BaseModel): auxiliary_id: Optional[int] = None alignment: Optional[str] = None appearance: Optional[int] = None + relations: Optional[List[dict]] = None class DeleteAvatarRequest(BaseModel): avatar_id: str @@ -883,7 +884,8 @@ def create_avatar(req: CreateAvatarRequest): technique=req.technique_id, weapon=req.weapon_id, auxiliary=req.auxiliary_id, - appearance=req.appearance + appearance=req.appearance, + relations=req.relations ) if req.pic_id is not None: @@ -899,10 +901,9 @@ def create_avatar(req: CreateAvatarRequest): if req.appearance is not None: avatar.appearance = get_appearance_by_level(req.appearance) - # 清空系统自动生成的关系,保持用户自定义角色独立 - existing_relations = list(getattr(avatar, "relations", {}).keys()) - for other in existing_relations: - avatar.clear_relation(other) + # 关系已经在 create_avatar_from_request 中根据参数设置好了, + # 且该函数内部调用 MortalPlanner 时已经指定 allow_relations=False,不会生成随机关系。 + # 因此这里不需要再清空关系,否则会把自己选的关系删掉。 if req.alignment: avatar.alignment = Alignment.from_str(req.alignment) diff --git a/src/sim/new_avatar.py b/src/sim/new_avatar.py index 3141fb4..36b56e0 100644 --- a/src/sim/new_avatar.py +++ b/src/sim/new_avatar.py @@ -859,6 +859,7 @@ def create_avatar_from_request( auxiliary: Union[str, int, Auxiliary, None] = None, personas: Union[str, int, Persona, List[Union[str, int, Persona]], None] = None, appearance: Optional[int] = None, + relations: Optional[List[Dict[str, str]]] = None, ) -> Avatar: """ 供前端使用的角色创建入口:支持字符串/ID 参数,且默认不生成亲友关系。 @@ -927,4 +928,28 @@ def create_avatar_from_request( overrides=overrides if overrides else None, ) + if relations: + for rel_item in relations: + target_id = rel_item.get('target_id') + rel_type = rel_item.get('relation') + + if not target_id or not rel_type: + continue + + # 尝试转为字符串ID + t_id_str = str(target_id) + target = world.avatar_manager.avatars.get(t_id_str) + if not target: + continue + + # 解析关系 + rel_enum = None + for r in Relation: + if r.value == rel_type: + rel_enum = r + break + + if rel_enum: + avatar.set_relation(target, rel_enum) + return avatar \ No newline at end of file diff --git a/web/src/api/game.ts b/web/src/api/game.ts index 3f2e367..48c5b98 100644 --- a/web/src/api/game.ts +++ b/web/src/api/game.ts @@ -47,6 +47,7 @@ export interface CreateAvatarParams { auxiliary_id?: number; alignment?: string; appearance?: number; + relations?: Array<{ target_id: string; relation: string }>; } export interface PhenomenonDTO { diff --git a/web/src/components/SystemMenu.vue b/web/src/components/SystemMenu.vue index ff20f5d..95c63f2 100644 --- a/web/src/components/SystemMenu.vue +++ b/web/src/components/SystemMenu.vue @@ -38,9 +38,21 @@ const createForm = ref({ weapon_id: undefined, auxiliary_id: undefined, alignment: undefined, - appearance: 7 + appearance: 7, + relations: [] }) +const relationOptions = [ + { label: '父母', value: 'parent' }, + { label: '子女', value: 'child' }, + { label: '兄弟姐妹', value: 'sibling' }, + { label: '师傅', value: 'master' }, + { label: '徒弟', value: 'apprentice' }, + { label: '道侣', value: 'lovers' }, + { label: '朋友', value: 'friend' }, + { label: '仇人', value: 'enemy' } +] + // --- Delete Avatar State --- const avatarList = ref([]) const avatarSearch = ref('') @@ -114,6 +126,24 @@ const alignmentOptions = computed(() => { })) }) +const avatarOptions = computed(() => { + return avatarList.value.map(a => ({ + label: `[${a.sect_name}] ${a.name}`, + value: a.id + })) +}) + +function addRelation() { + if (!createForm.value.relations) { + createForm.value.relations = [] + } + createForm.value.relations.push({ target_id: '', relation: 'friend' }) +} + +function removeRelation(index: number) { + createForm.value.relations?.splice(index, 1) +} + // --- Actions --- async function fetchSaves() { @@ -214,7 +244,8 @@ async function handleCreateAvatar() { weapon_id: undefined, auxiliary_id: undefined, alignment: undefined, - appearance: 7 + appearance: 7, + relations: [] } } catch (e) { message.error('创建失败: ' + String(e)) @@ -247,6 +278,7 @@ function switchTab(tab: typeof activeTab.value) { fetchSaves() } else if (tab === 'create') { fetchGameData() + fetchAvatarList() } else if (tab === 'delete') { fetchAvatarList() } @@ -395,6 +427,27 @@ onMounted(() => { + +
+
+ + + - +
+ + 添加关系 +
+
创建角色
@@ -753,4 +806,17 @@ onMounted(() => { text-align: right; color: #ddd; } + +.relations-container { + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; +} + +.relation-row { + display: flex; + gap: 8px; + align-items: center; +}