feat: 前端 UI 精修 + 后端视图调整 + 资源预置
前端: - world.html 游戏内聊天组件重构:表情选择器改固定方格+自适应列+去滚动条; 输入框清空占位文字+隐藏滚动条;聊天面板整体高度缩短约 1/3; 顶部用户区改为纯文字并点开个人信息弹窗(头像/头像框/性别/说明/称号/注册) - chat.html 聊天输入框压扁(46→34px) - codex.html 新增神话品质、天赋/技能书/书籍分类与预置数据 - rules.html 规则文档更新 后端(Odoo): - game_base 附件改为看板视图(图片/名称/备注/大小) - yt_world 玩家菜单直接指向 hr.employee - game_base 员工表单隐藏原生联系人/组织架构页签 - chat 模型/接口/视图与规则数据扩充 资源:生成 4x4 资源精灵表(树木/石材/芦苇/宝箱)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from .game_api import _json, _post_body
|
||||
from ..models.chat import GUIDE_NAME
|
||||
|
||||
|
||||
class ChatApiController(http.Controller):
|
||||
@@ -12,13 +13,14 @@ class ChatApiController(http.Controller):
|
||||
@http.route('/game/api/chat/channels', type='http', auth='public',
|
||||
methods=['GET'], csrf=False)
|
||||
def chat_channels(self, **kw):
|
||||
"""当前用户可见频道:世界频道(公开) + 自己参与的私聊/公会。"""
|
||||
"""当前用户可见频道:世界频道 + AI向导频道(均公开) + 自己参与的私聊/公会。"""
|
||||
uid = request.session.uid
|
||||
Channel = request.env['game.chat.channel'].sudo()
|
||||
domain = [('channel_type', '=', 'world')]
|
||||
public = [('channel_type', 'in', ['world', 'guide'])]
|
||||
if uid:
|
||||
domain = ['|', ('channel_type', '=', 'world'),
|
||||
('member_ids', 'in', [uid])]
|
||||
domain = ['|'] + public + [('member_ids', 'in', [uid])]
|
||||
else:
|
||||
domain = public
|
||||
recs = Channel.search_read(domain, ['id', 'name', 'channel_type'],
|
||||
order='sequence, id')
|
||||
return _json({'items': [{
|
||||
@@ -60,6 +62,7 @@ class ChatApiController(http.Controller):
|
||||
'create_date': str(r['create_date']),
|
||||
'is_self': bool(uid and r.get('author_id')
|
||||
and r['author_id'][0] == uid),
|
||||
'is_guide': bool(r['author_name'] == GUIDE_NAME),
|
||||
} for r in recs]
|
||||
return _json({'items': items})
|
||||
|
||||
@@ -92,6 +95,26 @@ class ChatApiController(http.Controller):
|
||||
'author_name': author_name,
|
||||
'body': body,
|
||||
})
|
||||
# AI 向导自动回复:仅对 guide 类型频道生效,与世界频道隔离
|
||||
guide_reply = None
|
||||
ch = request.env['game.chat.channel'].sudo().browse(channel_id)
|
||||
if ch.exists() and ch.channel_type == 'guide':
|
||||
reply_text = ch.generate_guide_reply(body)
|
||||
if reply_text:
|
||||
reply = request.env['game.chat.message'].sudo().create({
|
||||
'channel_id': channel_id,
|
||||
'author_id': False,
|
||||
'author_name': GUIDE_NAME,
|
||||
'body': reply_text,
|
||||
})
|
||||
guide_reply = {
|
||||
'id': reply.id,
|
||||
'author_name': GUIDE_NAME,
|
||||
'body': reply_text,
|
||||
'create_date': str(reply.create_date),
|
||||
'is_self': False,
|
||||
'is_guide': True,
|
||||
}
|
||||
return _json({
|
||||
'ok': True,
|
||||
'message': {
|
||||
@@ -100,5 +123,6 @@ class ChatApiController(http.Controller):
|
||||
'body': body,
|
||||
'create_date': str(msg.create_date),
|
||||
'is_self': True,
|
||||
}
|
||||
},
|
||||
'guide_reply': guide_reply,
|
||||
})
|
||||
|
||||
@@ -22,10 +22,18 @@ def _json(data):
|
||||
|
||||
def _post_body():
|
||||
try:
|
||||
raw = request.httprequest.get_data(as_text=True) or '{}'
|
||||
return json.loads(raw)
|
||||
# Odoo 官方解析 JSON 请求体(正确处理 UTF-8),优先使用。
|
||||
return request.get_json_data()
|
||||
except Exception:
|
||||
return {}
|
||||
try:
|
||||
# 兜底:显式按 UTF-8 解码原始字节,避免 Werkzeug 在 application/json
|
||||
# 无 charset 时按 latin1 解码中文导致乱码。
|
||||
raw = request.httprequest.get_data(cache=True) or b'{}'
|
||||
if isinstance(raw, str):
|
||||
raw = raw.encode('utf-8')
|
||||
return json.loads(raw.decode('utf-8'))
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
class GameApiController(http.Controller):
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- 默认世界频道:所有玩家可见、可发言 -->
|
||||
<data>
|
||||
<!-- 默认世界频道:所有玩家可见、可发言(纯净玩家聊天,不混入 AI 向导) -->
|
||||
<record model="game.chat.channel" id="channel_world">
|
||||
<field name="name">世界频道</field>
|
||||
<field name="channel_type">world</field>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
|
||||
<!-- 默认游戏助手频道:与世界频道物理隔离,玩家提问、系统自动回复(知识引擎) -->
|
||||
<record model="game.chat.channel" id="channel_guide">
|
||||
<field name="name">游戏助手</field>
|
||||
<field name="channel_type">guide</field>
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from odoo import models, fields
|
||||
|
||||
# 游戏助手的展示昵称(前后端共用,落库消息的作者名)
|
||||
GUIDE_NAME = '游戏助手'
|
||||
|
||||
|
||||
def _plain(text):
|
||||
"""去掉 HTML 标签,用于把 rule.desc(Html) 当作纯文本兜底。"""
|
||||
if not text:
|
||||
return ''
|
||||
return re.sub(r'<[^>]+>', '', text).strip()
|
||||
|
||||
|
||||
class GameChatChannel(models.Model):
|
||||
_name = 'game.chat.channel'
|
||||
@@ -12,12 +23,76 @@ class GameChatChannel(models.Model):
|
||||
('world', '世界频道'),
|
||||
('guild', '公会'),
|
||||
('pm', '私聊'),
|
||||
('guide', 'AI向导'),
|
||||
], string='类型', default='world', index=True)
|
||||
sequence = fields.Integer(string='排序', default=10)
|
||||
creator_id = fields.Many2one('res.users', string='创建者')
|
||||
member_ids = fields.Many2many('res.users', string='成员')
|
||||
message_ids = fields.One2many('game.chat.message', 'channel_id', string='消息')
|
||||
|
||||
def generate_guide_reply(self, question):
|
||||
"""内置游戏知识引擎(零外部依赖):按关键词检索 game.rule / game.rule.category 作答。
|
||||
预留扩展点:若需接入真实 LLM,替换本方法实现、返回模型生成的文本即可,其余调用方不变。
|
||||
"""
|
||||
q = (question or '').strip()
|
||||
if not q:
|
||||
return '你好,我是游戏助手。你可以问我关于「属性」「等级」「货币」「制度」的问题~'
|
||||
env = self.env
|
||||
Rule = env['game.rule']
|
||||
Cat = env['game.rule.category']
|
||||
|
||||
def rules_of(key):
|
||||
c = Cat.search([('key', '=', key)], limit=1)
|
||||
return Rule.search([('category_id', '=', c.id)], order='name') if c else []
|
||||
|
||||
def line(r):
|
||||
return f'· {r.name}:{r.summary or _plain(r.desc)}'
|
||||
|
||||
# 问候
|
||||
if re.search(r'(你好|您好|hi|hello|在吗|嗨|早上好|晚上好)', q, re.I):
|
||||
return ('你好,我是游戏助手 🤖 随时为你解答宇森世界的玩法。\n'
|
||||
'可以问我:属性怎么算?怎么升级?货币有什么用?有哪些制度?')
|
||||
|
||||
# 属性(关键词覆盖攻击/物攻/法攻/防御/暴击等;穿透/韧性已移除)
|
||||
_attr_re = re.search(r'(属性|攻击|物攻|法攻|防御|物防|法防|血量|生命|法力|速度|攻速|暴击|爆伤|命中|闪避|格挡|抗性|吸血|幸运)', q)
|
||||
if _attr_re:
|
||||
rules = list(rules_of('attribute_basic')) + list(rules_of('attribute_advanced'))
|
||||
if rules:
|
||||
head = '【属性规则】不同属性决定你的战斗表现,核心属性如下:'
|
||||
return head + '\n' + '\n'.join(line(r) for r in rules[:12]) + '\n\n想深入了解某项,直接问我它的名字~'
|
||||
|
||||
# 等级
|
||||
if re.search(r'(等级|升级|经验|世界等级|角色等级|怎么变强|变强)', q):
|
||||
for key in ('world', 'character'):
|
||||
rules = rules_of(key)
|
||||
if rules:
|
||||
c = Cat.search([('key', '=', key)], limit=1)
|
||||
head = f'【{c.name}】'
|
||||
lines = [f'· {r.name}(Lv{r.level}):{r.summary or ""}' for r in rules[:6]]
|
||||
return head + '\n' + '\n'.join(l for l in lines if l.strip())
|
||||
|
||||
# 货币
|
||||
if re.search(r'(货币|金币|钱|元宝|钻石|充值|兑换|经济)', q):
|
||||
rules = rules_of('currency')
|
||||
if rules:
|
||||
head = '【货币规则】游戏内货币与获取/消耗方式:'
|
||||
return head + '\n' + '\n'.join(line(r) for r in rules[:8])
|
||||
|
||||
# 制度 / 规则
|
||||
if re.search(r'(制度|规定|规则|条款|公约|不能|禁止|惩罚|封号)', q):
|
||||
rules = rules_of('system')
|
||||
if rules:
|
||||
head = '【制度说明】请遵守以下基本规则:'
|
||||
return head + '\n' + '\n'.join(line(r) for r in rules[:8])
|
||||
|
||||
# 兜底:给出可问的方向
|
||||
cats = Cat.search(
|
||||
[('key', 'in', ['world', 'character', 'currency', 'attribute', 'system'])],
|
||||
order='sequence')
|
||||
tips = '、'.join(c.name for c in cats) if cats else '属性、等级、货币、制度'
|
||||
return ('我是游戏助手,这个问题我还在学习中 🤔\n'
|
||||
f'你可以问我这些方向:{tips}。\n例如:「攻击和物防怎么算?」「怎么升级最快?」')
|
||||
|
||||
|
||||
class GameChatMessage(models.Model):
|
||||
_name = 'game.chat.message'
|
||||
|
||||
@@ -2,10 +2,52 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ============ 附件(Odoo 原生 ir.attachment) ============ -->
|
||||
<!-- ============ 附件(Odoo 原生 ir.attachment)看板视图 ============ -->
|
||||
<record id="yusen_attachment_kanban_view" model="ir.ui.view">
|
||||
<field name="name">yusen.attachment.kanban</field>
|
||||
<field name="model">ir.attachment</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field name="file_size"/>
|
||||
<field name="mimetype"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click oe_kanban_card d-flex flex-column p-2" style="width:160px;">
|
||||
<div class="o_kanban_image mb-2 text-center" style="height:100px;line-height:100px;background:rgba(0,0,0,.06);border-radius:6px;overflow:hidden;font-size:40px;">
|
||||
<t t-if="record.mimetype.raw_value and 'image' in record.mimetype.raw_value">🖼️</t>
|
||||
<t t-else="">📎</t>
|
||||
</div>
|
||||
<div class="oe_kanban_details text-center">
|
||||
<strong class="o_kanban_record_title d-block text-truncate" title="%s"><field name="name"/></strong>
|
||||
<div class="text-muted small text-truncate mt-1" style="min-height:1.2em;">
|
||||
<t t-if="record.description.raw_value"><field name="description"/></t>
|
||||
<t t-else="">—</t>
|
||||
</div>
|
||||
<div class="small mt-1" style="color:#6c757d;">
|
||||
<t t-if="record.file_size.raw_value >= 1048576">
|
||||
<t t-esc="(record.file_size.raw_value / 1048576).toFixed(2)"/> MB
|
||||
</t>
|
||||
<t t-elif="record.file_size.raw_value >= 1024">
|
||||
<t t-esc="(record.file_size.raw_value / 1024).toFixed(1)"/> KB
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="record.file_size.raw_value"/> B
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_yusen_attachment" model="ir.actions.act_window">
|
||||
<field name="name">附件</field>
|
||||
<field name="res_model">ir.attachment</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="view_mode">kanban,list,form</field>
|
||||
<field name="context">{'search_default_filter_website': 0}</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- ============ 世界聊天 · 顶级菜单 ============ -->
|
||||
<record id="menu_game_chat_root" model="ir.ui.menu">
|
||||
<field name="name">世界聊天</field>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
|
||||
<!-- ============ 世界聊天 · 频道 ============ -->
|
||||
<record id="action_game_chat_channel" model="ir.actions.act_window">
|
||||
<field name="name">世界聊天</field>
|
||||
<field name="name">频道</field>
|
||||
<field name="res_model">game.chat.channel</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<record id="menu_game_chat_channel" model="ir.ui.menu">
|
||||
<field name="name">频道</field>
|
||||
<field name="parent_id" ref="menu_game_chat_root"/>
|
||||
<field name="action" ref="action_game_chat_channel"/>
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
|
||||
<record id="view_game_chat_channel_list" model="ir.ui.view">
|
||||
<field name="name">game.chat.channel.list</field>
|
||||
<field name="model">game.chat.channel</field>
|
||||
@@ -46,4 +59,53 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============ 世界聊天 · 消息(后台记录与查看) ============ -->
|
||||
<record id="action_game_chat_message" model="ir.actions.act_window">
|
||||
<field name="name">聊天消息</field>
|
||||
<field name="res_model">game.chat.message</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'search_default_group_channel': 1}</field>
|
||||
</record>
|
||||
|
||||
<record id="view_game_chat_message_list" model="ir.ui.view">
|
||||
<field name="name">game.chat.message.list</field>
|
||||
<field name="model">game.chat.message</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="聊天消息" multi_edit="1">
|
||||
<field name="channel_id"/>
|
||||
<field name="author_name"/>
|
||||
<field name="body" string="内容"/>
|
||||
<field name="create_date"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_game_chat_message_form" model="ir.ui.view">
|
||||
<field name="name">game.chat.message.form</field>
|
||||
<field name="model">game.chat.message</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="channel_id"/>
|
||||
<field name="author_id"/>
|
||||
<field name="author_name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date"/>
|
||||
</group>
|
||||
<group string="内容">
|
||||
<field name="body" nolabel="1"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 频道菜单下挂消息子项,方便后台同步查看聊天记录 -->
|
||||
<record id="menu_game_chat_message" model="ir.ui.menu">
|
||||
<field name="name">聊天消息</field>
|
||||
<field name="parent_id" ref="menu_game_chat_root"/>
|
||||
<field name="action" ref="action_game_chat_message"/>
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- 隐藏原生「公开信息」(联系人) 和「HR 设置」(组织架构) 页签 -->
|
||||
<xpath expr="//page[@name='public']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='hr_settings']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<!-- 追加「游戏数据」页签(后续世界数据等关联也放这里) -->
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="游戏数据" name="game_data">
|
||||
<group>
|
||||
|
||||
@@ -442,15 +442,14 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ============ 玩家(res.users) ============ -->
|
||||
<!-- 独立顶级菜单:用 res.users 直接管理玩家,比 hr.employee 语义更准;
|
||||
view_mode 默认 list 起步,不进入"新建"表单。 -->
|
||||
<!-- ============ 玩家(hr.employee) ============ -->
|
||||
<!-- 直接用 hr.employee 管理玩家,点进去就是员工列表,无需经 res.users 中转 -->
|
||||
<record id="action_yt_player" model="ir.actions.act_window">
|
||||
<field name="name">玩家</field>
|
||||
<field name="res_model">res.users</field>
|
||||
<field name="res_model">hr.employee</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'search_default_active': 1, 'search_default_share': 0}</field>
|
||||
<field name="help">游戏玩家列表。点「玩家世界」菜单可按用户筛其下属的全部世界/地块/微观/角色/生物等数据。</field>
|
||||
<field name="context">{'search_default_active': 1}</field>
|
||||
<field name="help">游戏玩家(员工)列表。每位玩家对应一个 hr.employee,关联其 Odoo 登录账户。</field>
|
||||
</record>
|
||||
|
||||
<record id="view_yt_player_search" model="ir.ui.view">
|
||||
|
||||
@@ -124,4 +124,132 @@
|
||||
<field name="note"><![CDATA[至高货币,不可增发。总量由世界本源锚定,仅可通过特定事件获取。]]></field>
|
||||
</record>
|
||||
|
||||
<!-- ====================================================================
|
||||
属性规则(战斗 / 成长核心数值维度)
|
||||
说明:前端「规则」页的「属性规则」页签从此处读取。
|
||||
为支持「基础 / 高级」分区,属性拆为两个类别:
|
||||
attribute_basic (基础属性,seq=50)
|
||||
attribute_advanced(高级属性,seq=51)
|
||||
前端会把两者合并到同一个「属性规则」页签内,分两组展示。
|
||||
每条属性 = 一条 game.rule:
|
||||
name = 属性中文名(前端按名映射 emoji 图标)
|
||||
desc = 给玩家的说明(是什么、怎么影响战局)
|
||||
summary = 设计师基准(成长来源 / 公式提示,后台可见)
|
||||
物攻 / 法攻决定输出,防御类(防御 / 物防 / 法防)按伤害类型减伤,
|
||||
其余为速度、暴击、爆伤、命中闪避等衍生维度,共同构成单位战力模型。
|
||||
==================================================================== -->
|
||||
<record id="rule_category_attribute_basic" model="game.rule.category">
|
||||
<field name="key">attribute_basic</field>
|
||||
<field name="name">基础属性</field>
|
||||
<field name="sequence">50</field>
|
||||
</record>
|
||||
<record id="rule_category_attribute_advanced" model="game.rule.category">
|
||||
<field name="key">attribute_advanced</field>
|
||||
<field name="name">高级属性</field>
|
||||
<field name="sequence">51</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== 基础属性 ===== -->
|
||||
<!-- 生存 -->
|
||||
<record id="attr_hp" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">生命值</field>
|
||||
<field name="desc"><![CDATA[单位存活的血量上限。受到伤害时优先扣除,归零则该单位倒下。随等级、装备与回复技能提升。]]></field>
|
||||
<field name="summary"><![CDATA[基础值随等级成长,装备/天赋加成]]></field>
|
||||
</record>
|
||||
<record id="attr_mp" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">法力值</field>
|
||||
<field name="desc"><![CDATA[释放技能与法术的消耗资源。普攻不耗蓝,技能按强度扣蓝;蓝量不足无法施法,脱战自然回复。]]></field>
|
||||
<field name="summary"><![CDATA[技能消耗资源,脱战回复]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 攻击 -->
|
||||
<record id="attr_atk" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">物攻</field>
|
||||
<field name="desc"><![CDATA[物理输出能力,决定普通攻击与物理系技能的基础伤害。越高打出越疼。]]></field>
|
||||
<field name="summary"><![CDATA[物理伤害基数]]></field>
|
||||
</record>
|
||||
<record id="attr_mat" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">法攻</field>
|
||||
<field name="desc"><![CDATA[法术输出能力,决定法术技能的基础伤害。与物攻相互独立,受法强装备加成。]]></field>
|
||||
<field name="summary"><![CDATA[法术伤害基数]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 防御 -->
|
||||
<record id="attr_def" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">防御</field>
|
||||
<field name="desc"><![CDATA[通用物理减伤能力,是对物理伤害的笼统防护,多数装备与体质提供。与「物防」同源,物防为其专精项。]]></field>
|
||||
<field name="summary"><![CDATA[物理减伤(通用)]]></field>
|
||||
</record>
|
||||
<record id="attr_pdef" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">物防</field>
|
||||
<field name="desc"><![CDATA[针对物理伤害的专项减免,由护甲、盾牌等装备提供。面对物理系敌人时主要看此项。]]></field>
|
||||
<field name="summary"><![CDATA[物理伤害减免]]></field>
|
||||
</record>
|
||||
<record id="attr_mdef" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">法防</field>
|
||||
<field name="desc"><![CDATA[针对法术伤害的专项减免,由法抗装备、抗性符文提供。面对法师 / 术士时主要看此项。]]></field>
|
||||
<field name="summary"><![CDATA[法术伤害减免]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 节奏 -->
|
||||
<record id="attr_spd" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">速度</field>
|
||||
<field name="desc"><![CDATA[行动先后顺序与出手频率。速度高者先手,回合制中更频繁行动;也影响部分先攻技能触发。]]></field>
|
||||
<field name="summary"><![CDATA[先攻 / 行动顺序]]></field>
|
||||
</record>
|
||||
<record id="attr_aspd" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_basic"/><field name="level">0</field><field name="name">攻速</field>
|
||||
<field name="desc"><![CDATA[两次攻击的间隔,越短出手越快、单位时间输出越高。影响普攻与部分技能频率。]]></field>
|
||||
<field name="summary"><![CDATA[攻击间隔]]></field>
|
||||
</record>
|
||||
|
||||
<!-- ===== 高级属性 ===== -->
|
||||
<!-- 暴击 -->
|
||||
<record id="attr_crt" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">暴击</field>
|
||||
<field name="desc"><![CDATA[造成暴击的概率(百分比)。暴击时伤害按「爆伤」倍率提升。]]></field>
|
||||
<field name="summary"><![CDATA[暴击率 %]]></field>
|
||||
</record>
|
||||
<record id="attr_crtdmg" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">爆伤</field>
|
||||
<field name="desc"><![CDATA[暴击时的伤害倍率(默认 150%)。暴击率越高,堆此项收益越大。]]></field>
|
||||
<field name="summary"><![CDATA[暴击倍率 %]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 命中 / 闪避 -->
|
||||
<record id="attr_acc" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">命中</field>
|
||||
<field name="desc"><![CDATA[攻击命中的概率。面对高闪避目标需足够命中,否则出现「未命中」。]]></field>
|
||||
<field name="summary"><![CDATA[命中率 %]]></field>
|
||||
</record>
|
||||
<record id="attr_eva" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">闪避</field>
|
||||
<field name="desc"><![CDATA[躲避攻击的概率。高闪避可让对手攻击落空,但受对方命中压制。]]></field>
|
||||
<field name="summary"><![CDATA[闪避率 %]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 减伤 -->
|
||||
<record id="attr_blk" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">格挡</field>
|
||||
<field name="desc"><![CDATA[受击时触发格挡、按比例减免伤害的概率(需持盾 / 特定姿态)。格挡成功大幅减伤。]]></field>
|
||||
<field name="summary"><![CDATA[格挡率 %]]></field>
|
||||
</record>
|
||||
|
||||
<!-- 异常 / 续航 -->
|
||||
<record id="attr_res" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">抗性</field>
|
||||
<field name="desc"><![CDATA[对灼烧、冰冻、中毒等异常状态的抵抗概率。高抗性降低被控与持续掉血风险。]]></field>
|
||||
<field name="summary"><![CDATA[异常抵抗 %]]></field>
|
||||
</record>
|
||||
<record id="attr_lifesteal" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">吸血</field>
|
||||
<field name="desc"><![CDATA[造成伤害时按百分比回复自身生命。持续作战与单挑续航的关键属性。]]></field>
|
||||
<field name="summary"><![CDATA[吸血 %]]></field>
|
||||
</record>
|
||||
<record id="attr_luck" model="game.rule">
|
||||
<field name="category_id" ref="rule_category_attribute_advanced"/><field name="level">0</field><field name="name">幸运</field>
|
||||
<field name="desc"><![CDATA[影响暴击、掉落与稀有事件触发概率的隐性维度。高幸运带来更多意外之喜与关键转折,是冒险与博弈中的变数之源。]]></field>
|
||||
<field name="summary"><![CDATA[幸运值(影响暴击/掉落/稀有事件)]]></field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user