89 lines
3.6 KiB
Python
89 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
from odoo import models, fields
|
||
import random
|
||
|
||
|
||
class YtWorld(models.Model):
|
||
"""玩家世界:每个用户一条。seed 再生基础地图,tile_ids 存被改地块。"""
|
||
_name = 'yt.world'
|
||
_description = '玩家世界'
|
||
|
||
name = fields.Char(string='世界名称', translate=True)
|
||
user_id = fields.Many2one('res.users', string='拥有者',
|
||
required=True, ondelete='cascade', index=True)
|
||
seed = fields.Integer(string='生成种子', required=True, index=True)
|
||
world_version = fields.Integer(string='生成算法版本', default=1)
|
||
settings = fields.Json(string='世界设置') # 相机位/缩放/UI 偏好
|
||
tile_ids = fields.One2many('yt.world.tile', 'world_id', string='地表地块')
|
||
created_at = fields.Datetime(string='创建时间', default=fields.Datetime.now)
|
||
last_active = fields.Datetime(string='最后活跃')
|
||
|
||
# ---------- 接口方法(供 controller 调用) ----------
|
||
|
||
def get_or_create_for_user(self, user):
|
||
"""取当前用户的世界;没有则按随机 seed 新建。"""
|
||
rec = self.search([('user_id', '=', user.id)], limit=1)
|
||
if not rec:
|
||
rec = self.create({
|
||
'user_id': user.id,
|
||
'seed': random.randint(1, 2_000_000_000),
|
||
'name': '我的世界',
|
||
})
|
||
rec.last_active = fields.Datetime.now()
|
||
return rec
|
||
|
||
def export_state(self):
|
||
"""导出前端需要的世界状态:seed + 地块覆盖列表。"""
|
||
self.ensure_one()
|
||
tiles = []
|
||
for t in self.tile_ids:
|
||
tiles.append({
|
||
'q': t.q,
|
||
'r': t.r,
|
||
'terrain': t.terrain or None,
|
||
'height': t.height or None,
|
||
'flags': t.flags or {},
|
||
'micro_world_id': t.micro_world_id.id if t.micro_world_id else None,
|
||
})
|
||
return {
|
||
'world_id': self.id,
|
||
'seed': self.seed,
|
||
'world_version': self.world_version,
|
||
'settings': self.settings or {},
|
||
'tiles': tiles,
|
||
}
|
||
|
||
def import_tiles(self, tiles):
|
||
"""增量覆盖:upsert 地块。terrain/height/flags 全回退底图则删除覆盖行。"""
|
||
self.ensure_one()
|
||
tile_model = self.env['yt.world.tile']
|
||
for tv in (tiles or []):
|
||
q, r = tv.get('q'), tv.get('r')
|
||
if q is None or r is None:
|
||
continue
|
||
terrain = tv.get('terrain')
|
||
height = tv.get('height')
|
||
flags = tv.get('flags')
|
||
# 全部回退底图 → 删覆盖行,保持最小集(种子+增量覆盖原则)
|
||
if not terrain and height is None and not (flags or {}):
|
||
existing = tile_model.search([
|
||
('world_id', '=', self.id), ('q', '=', q), ('r', '=', r)])
|
||
if existing:
|
||
existing.unlink()
|
||
continue
|
||
vals = {}
|
||
if terrain is not None:
|
||
vals['terrain'] = terrain
|
||
if height is not None:
|
||
vals['height'] = height
|
||
if flags is not None:
|
||
vals['flags'] = flags
|
||
existing = tile_model.search([
|
||
('world_id', '=', self.id), ('q', '=', q), ('r', '=', r)])
|
||
if existing:
|
||
existing.write(vals)
|
||
else:
|
||
vals.update({'q': q, 'r': r, 'world_id': self.id})
|
||
tile_model.create(vals) # create 钩子自动建微观占位
|
||
self.last_active = fields.Datetime.now()
|