任务系统,新手引导
This commit is contained in:
@@ -340,10 +340,157 @@ class YtWorldApi(http.Controller):
|
||||
char = request.env['yt.world.character'].sudo().search(
|
||||
[('world_id', '=', world.id)], limit=1)
|
||||
if not char:
|
||||
char = request.env['yt.world.character'].sudo().create(
|
||||
{'world_id': world.id})
|
||||
# 认领当前登录人的玩家档案(hr.employee),把注册昵称同步为角色名
|
||||
vals = {'world_id': world.id}
|
||||
uid = request.session.uid
|
||||
if uid:
|
||||
emp = request.env['hr.employee'].sudo().search(
|
||||
[('user_id', '=', uid)], limit=1)
|
||||
if emp:
|
||||
vals['employee_id'] = emp.id
|
||||
vals['name'] = emp.name or char.name
|
||||
char = request.env['yt.world.character'].sudo().create(vals)
|
||||
else:
|
||||
# 兼容老数据:已存在角色但没关联 employee 时补上
|
||||
if not char.employee_id:
|
||||
uid = request.session.uid
|
||||
if uid:
|
||||
emp = request.env['hr.employee'].sudo().search(
|
||||
[('user_id', '=', uid)], limit=1)
|
||||
if emp:
|
||||
char.sudo().write({'employee_id': emp.id,
|
||||
'name': emp.name or char.name})
|
||||
return char
|
||||
|
||||
# ========== 任务系统序列化(get_tasks / task_progress 共用) ==========
|
||||
def _serialize_tasks(self, char):
|
||||
"""把任务定义 + 角色进度汇总成前端面板可直接渲染的结构。"""
|
||||
done_codes = [t['code'] for t in (char.tasks or []) if t.get('state') == 'done']
|
||||
tasks = request.env['yt.world.task'].sudo().search(
|
||||
[('active', '=', True)], order='chapter, sequence, id')
|
||||
out = []
|
||||
for t in tasks:
|
||||
prereq = t.prereq or []
|
||||
entry = None
|
||||
for x in (char.tasks or []):
|
||||
if x.get('code') == t.code:
|
||||
entry = x
|
||||
break
|
||||
if entry and entry.get('state') == 'done':
|
||||
state = 'done'
|
||||
elif any(p not in done_codes for p in prereq):
|
||||
state = 'locked'
|
||||
elif entry and entry.get('state') == 'claimable':
|
||||
state = 'claimable'
|
||||
else:
|
||||
state = 'in_progress'
|
||||
progress = (entry or {}).get('progress') or {}
|
||||
objs = []
|
||||
for o in (t.objectives or []):
|
||||
oid = o.get('id') or ('%s:%s' % (o.get('type'), o.get('key')))
|
||||
need = o.get('count', 1) or 1
|
||||
have = min(progress.get(oid, 0), need)
|
||||
objs.append({
|
||||
'type': o.get('type'), 'key': o.get('key'),
|
||||
'label': o.get('label'), 'hint': o.get('hint'),
|
||||
'have': have, 'need': need,
|
||||
})
|
||||
out.append({
|
||||
'code': t.code, 'chapter': t.chapter, 'title': t.title,
|
||||
'desc': t.desc or '', 'objectives': objs,
|
||||
'reward_exp': t.reward_exp or 0,
|
||||
'reward_res': t.reward_res or {},
|
||||
'prereq': prereq, 'state': state,
|
||||
})
|
||||
chapters = sorted({t['chapter'] for t in out})
|
||||
return chapters, out
|
||||
|
||||
@http.route('/yt_world/api/tasks', type='http', auth='public',
|
||||
methods=['GET', 'OPTIONS'], csrf=False)
|
||||
def get_tasks(self, **kw):
|
||||
"""返回任务定义 + 当前角色进度,前端据此渲染面板。"""
|
||||
auth_err = self._require_user()
|
||||
if auth_err:
|
||||
return _json(auth_err)
|
||||
world = self._get_world()
|
||||
if not world:
|
||||
return _json({'ok': False, 'error': 'no_world'})
|
||||
char = self._get_char(world)
|
||||
# 回溯校验:角色等级可能已满足 level_up 类型任务,补发事件
|
||||
char.on_game_event('level_up', {'level': char.level})
|
||||
chapters, out = self._serialize_tasks(char)
|
||||
return _json({
|
||||
'ok': True,
|
||||
'chapters': chapters,
|
||||
'tasks': out,
|
||||
'char': {
|
||||
'name': char.name, 'level': char.level, 'exp': char.exp,
|
||||
'exp_max': char.exp_max, 'gold': char.gold,
|
||||
},
|
||||
})
|
||||
|
||||
@http.route('/yt_world/api/task/progress', type='http', auth='public',
|
||||
methods=['POST', 'OPTIONS'], csrf=False)
|
||||
def task_progress(self, **kw):
|
||||
"""接收前端游戏事件并推进任务进度。body: {"event":"build_placed","payload":{"code":"farmland"}}
|
||||
返回更新后的任务列表(与 /tasks 同结构),claimable 列出本次新可领取的任务。"""
|
||||
auth_err = self._require_user()
|
||||
if auth_err:
|
||||
return _json(auth_err)
|
||||
world = self._get_world()
|
||||
if not world:
|
||||
return _json({'ok': False, 'error': 'no_world'})
|
||||
body = _post_body()
|
||||
event = (body.get('event') or '').strip()
|
||||
payload = body.get('payload') or {}
|
||||
if not event:
|
||||
return _json({'ok': False, 'error': 'bad_params'})
|
||||
char = self._get_char(world)
|
||||
res = char.on_game_event(event, payload)
|
||||
# 回溯校验:角色等级可能已满足 level_up 类型任务,补发事件
|
||||
char.on_game_event('level_up', {'level': char.level})
|
||||
chapters, out = self._serialize_tasks(char)
|
||||
return _json({
|
||||
'ok': True,
|
||||
'chapters': chapters,
|
||||
'tasks': out,
|
||||
'char': {
|
||||
'name': char.name, 'level': char.level, 'exp': char.exp,
|
||||
'exp_max': char.exp_max, 'gold': char.gold,
|
||||
},
|
||||
'claimable': res.get('claimable', []),
|
||||
})
|
||||
|
||||
@http.route('/yt_world/api/task/complete', type='http', auth='public',
|
||||
methods=['POST', 'OPTIONS'], csrf=False)
|
||||
def complete_task(self, **kw):
|
||||
"""领取/完成任务。body: {"code": "ch1_welcome"}
|
||||
校验前置→发奖→写进度,返回最新角色状态。"""
|
||||
auth_err = self._require_user()
|
||||
if auth_err:
|
||||
return _json(auth_err)
|
||||
world = self._get_world()
|
||||
if not world:
|
||||
return _json({'ok': False, 'error': 'no_world'})
|
||||
body = _post_body()
|
||||
code = (body.get('code') or '').strip()
|
||||
if not code:
|
||||
return _json({'ok': False, 'error': 'bad_params'})
|
||||
char = self._get_char(world)
|
||||
res = char.complete_task(code)
|
||||
if not res.get('ok'):
|
||||
return _json(res)
|
||||
return _json({
|
||||
'ok': True,
|
||||
'already': res.get('already', False),
|
||||
'char': {
|
||||
'name': char.name, 'level': char.level, 'exp': char.exp,
|
||||
'exp_max': char.exp_max, 'gold': char.gold,
|
||||
},
|
||||
'reward_exp': res.get('reward_exp', 0),
|
||||
'reward_res': res.get('reward_res', {}),
|
||||
})
|
||||
|
||||
@http.route('/yt_world/api/char', type='http', auth='public',
|
||||
methods=['GET', 'OPTIONS'], csrf=False)
|
||||
def get_char(self, **kw):
|
||||
@@ -358,7 +505,8 @@ class YtWorldApi(http.Controller):
|
||||
return _json({
|
||||
'ok': True,
|
||||
'name': char.name, 'level': char.level, 'exp': char.exp,
|
||||
'gold': char.gold, 'inventory': char.inventory or {},
|
||||
'exp_max': char.exp_max or 100, 'gold': char.gold,
|
||||
'inventory': char.inventory or {},
|
||||
'equip': char.equip or {'weapon': None, 'armor': None, 'accessory': None},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user