宝箱站位优化
This commit is contained in:
@@ -90,8 +90,12 @@ class YtWorldChest(models.Model):
|
||||
# 删掉最旧的,腾出一个空位给新宝箱
|
||||
to_remove = existing[:len(existing) - MAX_CHESTS_PER_TILE + 1]
|
||||
to_remove.unlink()
|
||||
# 2) 占用格集合(建筑 footprint)
|
||||
# 2) 占用格集合(建筑 footprint + 已有 active 宝箱格子)
|
||||
# 宝箱作为 1×1 建筑:不能和建筑重叠,也不能多个宝箱叠在同一格
|
||||
occ = self._occupied_cells(world, q, r, grid_n)
|
||||
keep_chests = [c for c in existing if c not in to_remove]
|
||||
chest_cells = {(int(c.cell_i), int(c.cell_j)) for c in keep_chests}
|
||||
occ |= chest_cells
|
||||
# 3) 随机空格(留 1 格边距,防止贴边/溢出岛外)
|
||||
margin = 1
|
||||
free = []
|
||||
|
||||
+31
-12
@@ -2780,7 +2780,13 @@ async function microLoadChests(q, r) {
|
||||
try {
|
||||
const j = await API.chests(q, r);
|
||||
if (j && j.ok && Array.isArray(j.chests)) {
|
||||
microChests = j.chests.map(c => ({ ...c, _claiming: false }));
|
||||
// 同格去重:宝箱为 1×1 建筑,每格只保留一个(防御性,避免后端异常时叠箱)
|
||||
const seen = new Set();
|
||||
microChests = j.chests.filter(c => {
|
||||
const k = Math.round(c.cell_i) + ',' + Math.round(c.cell_j);
|
||||
if (seen.has(k)) return false;
|
||||
seen.add(k); return true;
|
||||
}).map(c => ({ ...c, _claiming: false }));
|
||||
window.microChests = microChests; // 同步给 characters.js(否则其读到的是旧空数组引用)
|
||||
}
|
||||
} catch (e) { /* 离线兜底 */ }
|
||||
@@ -2868,27 +2874,32 @@ function getChestImage(url) {
|
||||
function microChestTopImage(tier) { return MICRO_CHEST_TOP_IMAGES[tier] || null; }
|
||||
function microChestIconImage(tier) { return MICRO_CHEST_ICON_IMAGES[tier] || null; }
|
||||
|
||||
// 在网格层之上绘制所有活跃宝箱(呼吸光晕 + 漂浮主体/俯视图)
|
||||
// 在网格层之上绘制所有活跃宝箱。
|
||||
// 宝箱 = 1×1 建筑:俯视图素材本身已是等距 3D 视角(无需再做 iso 投影),
|
||||
// 直接以格心为原点绘制、clip 到 1×1 菱形 footprint 防溢出。
|
||||
function microDrawChests(c) {
|
||||
if (!microChests.length) return;
|
||||
const t = microState.animTime;
|
||||
const g = microBuildGeom();
|
||||
const cell = g.cell, hw = cell / 2, hh = g.hh; // 单格菱形半宽 hw、半高 hh
|
||||
microChests.forEach(ch => {
|
||||
const sp = microCellToScreen(ch.cell_i + 0.5, ch.cell_j + 0.5);
|
||||
// 主体:优先使用俯视图 PNG,未加载成功则回退 emoji
|
||||
const sp = microCellToScreen(ch.cell_i + 0.5, ch.cell_j + 0.5); // 1×1 占位中心 = 格心
|
||||
const imgUrl = microChestTopImage(ch.tier);
|
||||
const img = imgUrl ? getChestImage(imgUrl) : null;
|
||||
if (img && img.complete && img.naturalWidth) {
|
||||
const baseW = 42, baseH = 42;
|
||||
c.save();
|
||||
c.translate(sp.x, sp.y - 14);
|
||||
// 45度俯视:轻微压扁并旋转,贴合等距地面
|
||||
c.scale(1, 0.72);
|
||||
c.drawImage(img, -baseW / 2, -baseH / 2, baseW, baseH);
|
||||
c.translate(sp.x, sp.y);
|
||||
// 1×1 菱形 footprint clip(防溢出到相邻格子)
|
||||
c.beginPath();
|
||||
c.moveTo(0, -hh); c.lineTo(hw, 0); c.lineTo(0, hh); c.lineTo(-hw, 0); c.closePath();
|
||||
c.clip();
|
||||
// 俯视图本身已是 3D 等距视角,直接按格子比例缩放绘制,不再做 rotate/scale 投影
|
||||
const s = cell * 0.46; // 缩小到完整落入单格菱形内,不被 clip 裁切
|
||||
c.drawImage(img, -s / 2, -s / 2, s, s);
|
||||
c.restore();
|
||||
} else {
|
||||
c.save();
|
||||
c.translate(sp.x, sp.y - 14);
|
||||
c.font = '30px sans-serif'; c.textAlign = 'center'; c.textBaseline = 'middle';
|
||||
c.translate(sp.x, sp.y);
|
||||
c.font = '28px sans-serif'; c.textAlign = 'center'; c.textBaseline = 'middle';
|
||||
c.fillText(microChestEmoji(ch.tier), 0, 0);
|
||||
c.restore();
|
||||
}
|
||||
@@ -7268,6 +7279,14 @@ function microFootprintHit(list, i, j, ew, eh, ignore) {
|
||||
const c = microItemCells(o);
|
||||
if (i < o.i + c.ew && i + ew > o.i && j < o.j + c.eh && j + eh > o.j) return true;
|
||||
}
|
||||
// 宝箱作为 1×1 建筑占用格:不在建筑栏,但生成出来占网格,建造时不能放到宝箱所在格
|
||||
if (typeof microChests !== 'undefined' && microChests && microChests.length) {
|
||||
for (const ch of microChests) {
|
||||
if (ch === ignore) continue;
|
||||
const ci = Math.round(ch.cell_i), cj = Math.round(ch.cell_j);
|
||||
if (i < ci + 1 && i + ew > ci && j < cj + 1 && j + eh > cj) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// 可见矩形(内缩后)在 (i,j) 连续坐标下的覆盖范围,用于约束建筑落点不出框
|
||||
|
||||
Reference in New Issue
Block a user