125 lines
5.1 KiB
Python
125 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
import json
|
||
from odoo import http
|
||
from odoo.http import request
|
||
from datetime import datetime
|
||
|
||
def _json(data):
|
||
return request.make_response(
|
||
json.dumps(data, ensure_ascii=False, default=str),
|
||
headers=[('Content-Type', 'application/json; charset=utf-8')]
|
||
)
|
||
|
||
|
||
class GameNpcApi(http.Controller):
|
||
|
||
# ── NPC 列表 ──
|
||
@http.route('/game/api/npcs', type='http', auth='public', methods=['GET'], csrf=False)
|
||
def npc_list(self, **kw):
|
||
"""返回所有活跃 NPC(含当前活动和关键状态)。"""
|
||
npcs = request.env['game.npc'].sudo().search_read(
|
||
[('active', '=', True)],
|
||
['id', 'name', 'title', 'code', 'archetype', 'personality',
|
||
'current_activity', 'energy', 'hunger', 'safety', 'mood',
|
||
'mood_label', 'activity_label', 'goal_progress', 'current_goal',
|
||
'current_area', 'relationship_count', 'recent_log', 'tick_count',
|
||
'affiliation'],
|
||
order='sequence, id'
|
||
)
|
||
for n in npcs:
|
||
n['archetype_label'] = dict(
|
||
request.env['game.npc']._fields['archetype'].selection
|
||
).get(n['archetype'], n['archetype'])
|
||
|
||
return _json({'npcs': npcs})
|
||
|
||
# ── NPC 详情 ──
|
||
@http.route('/game/api/npc/<int:npc_id>', type='http', auth='public', methods=['GET'], csrf=False)
|
||
def npc_detail(self, npc_id, **kw):
|
||
"""NPC 详情(含关系网 + 最近行为 + 知识)。"""
|
||
Npc = request.env['game.npc'].sudo()
|
||
npc = Npc.browse(npc_id)
|
||
if not npc.exists():
|
||
return _json({'error': 'NPC 不存在'})
|
||
|
||
data = npc.read([
|
||
'id', 'name', 'title', 'code', 'archetype', 'personality',
|
||
'origin', 'bio', 'current_activity', 'energy', 'hunger', 'safety',
|
||
'mood', 'mood_label', 'activity_label', 'goal_progress', 'current_goal',
|
||
'goal_detail', 'current_area', 'tick_count', 'last_tick',
|
||
'affiliation',
|
||
])
|
||
if not data:
|
||
return _json({'error': 'NPC 不存在'})
|
||
data = data[0]
|
||
|
||
# 关系网
|
||
rels = request.env['game.npc.relationship'].sudo().search_read(
|
||
[('npc_id', '=', npc_id)],
|
||
['target_npc_id', 'affinity', 'status', 'last_interaction'],
|
||
order='affinity desc'
|
||
)
|
||
data['relationships'] = [{
|
||
'target_id': r['target_npc_id'][0] if r['target_npc_id'] else None,
|
||
'target_name': r['target_npc_id'][1] if r['target_npc_id'] else '',
|
||
'affinity': r['affinity'],
|
||
'status': r['status'],
|
||
} for r in rels]
|
||
|
||
# 最近行为(20 条)
|
||
logs = request.env['game.npc.behavior.log'].sudo().search_read(
|
||
[('npc_id', '=', npc_id)],
|
||
['tick_time', 'activity', 'activity_label', 'location', 'summary', 'target_npc_id'],
|
||
limit=20, order='tick_time desc'
|
||
)
|
||
data['recent_behaviors'] = [{
|
||
'time': str(l['tick_time']),
|
||
'activity': l['activity'],
|
||
'label': l['activity_label'],
|
||
'location': l['location'] or '',
|
||
'summary': l['summary'],
|
||
} for l in logs]
|
||
|
||
# 知识
|
||
knowledge = request.env['game.npc.knowledge'].sudo().search_read(
|
||
[('npc_id', '=', npc_id)],
|
||
['topic', 'content', 'accuracy', 'source'],
|
||
order='sequence, id'
|
||
)
|
||
data['knowledge'] = knowledge
|
||
|
||
return _json(data)
|
||
|
||
# ── NPC 关系网 ──
|
||
@http.route('/game/api/npc/<int:npc_id>/relationships', type='http', auth='public', methods=['GET'], csrf=False)
|
||
def npc_relationships(self, npc_id, **kw):
|
||
rels = request.env['game.npc.relationship'].sudo().search_read(
|
||
[('npc_id', '=', npc_id)],
|
||
['target_npc_id', 'affinity', 'status', 'last_interaction', 'interaction_count'],
|
||
order='affinity desc'
|
||
)
|
||
return _json({'relationships': [{
|
||
'target_id': r['target_npc_id'][0] if r['target_npc_id'] else None,
|
||
'target_name': r['target_npc_id'][1] if r['target_npc_id'] else '',
|
||
'affinity': r['affinity'],
|
||
'status': r['status'],
|
||
'last_interaction': str(r['last_interaction']),
|
||
'interaction_count': r['interaction_count'],
|
||
} for r in rels]})
|
||
|
||
# ── 主动触发单个 NPC Tick ──
|
||
@http.route('/game/api/npc/<int:npc_id>/tick', type='http', auth='user', methods=['POST'], csrf=False)
|
||
def npc_tick(self, npc_id, **kw):
|
||
npc = request.env['game.npc'].sudo().browse(npc_id)
|
||
if not npc.exists():
|
||
return _json({'error': 'NPC 不存在'})
|
||
npc.action_tick()
|
||
return _json({'ok': True, 'name': npc.name, 'tick_count': npc.tick_count,
|
||
'activity': npc.activity_label, 'mood': npc.mood_label})
|
||
|
||
# ── 批量 Tick ──
|
||
@http.route('/game/api/npc/tick-all', type='http', auth='user', methods=['POST'], csrf=False)
|
||
def npc_tick_all(self, **kw):
|
||
count = request.env['game.npc'].sudo().cron_batch_tick(batch_size=100)
|
||
return _json({'ok': True, 'ticked': count})
|