hm
This commit is contained in:
parent
cefa2ace5a
commit
0e432c62ae
87 changed files with 4282 additions and 3066 deletions
130
js/pin_attachment_system.js
Normal file
130
js/pin_attachment_system.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
"use strict";
|
||||
|
||||
// Layer: entity-runtime/items/pin-attachment
|
||||
// Centralizes pushpin/oshibyo attachment geometry, target classification, and
|
||||
// render attachment placement. Motion/effects still live in dynamic systems.
|
||||
(function (global) {
|
||||
function isPin(itemOrType) {
|
||||
const type = typeof itemOrType === "string" ? itemOrType : itemOrType?.type;
|
||||
return Boolean(type && typeof global.isPinType === "function" && global.isPinType(type));
|
||||
}
|
||||
function behaviorFor(itemOrType) {
|
||||
const type = typeof itemOrType === "string" ? itemOrType : itemOrType?.type;
|
||||
return typeof global.pinBehaviorFor === "function" ? global.pinBehaviorFor(type) : null;
|
||||
}
|
||||
function isOshibyo(itemOrType) {
|
||||
return Boolean(behaviorFor(itemOrType)?.blocksZunchi);
|
||||
}
|
||||
function tarinaiVisualSide(item, tarinai) {
|
||||
const faceDir = tarinai?.facingDir ? tarinai.facingDir() : (tarinai?.facing || 1);
|
||||
const faceSide = faceDir >= 0 ? 1 : -1;
|
||||
return isOshibyo(item) ? -faceSide : faceSide;
|
||||
}
|
||||
function tarinaiAnchor(item, tarinai, opts = {}) {
|
||||
const r = tarinai?.radius || 16;
|
||||
const visualSide = Number.isFinite(opts.visualSide) ? opts.visualSide : tarinaiVisualSide(item, tarinai);
|
||||
if (isOshibyo(item)) {
|
||||
const sprite = String(tarinai?.visibleSpriteId || (tarinai?.rawSpriteId ? tarinai.rawSpriteId() : "") || "");
|
||||
const backSprite = sprite === "back" || sprite === "tarinai12_back" || /(^|_)back$/i.test(sprite);
|
||||
const xFactor = backSprite ? 0.62 : 0.96;
|
||||
const yFactor = backSprite ? 0.28 : 0.50;
|
||||
const angleFactor = backSprite ? 0.32 : 0.48;
|
||||
const distFactor = backSprite ? 0.70 : 0.96;
|
||||
return {
|
||||
visualSide,
|
||||
x: (tarinai?.x || 0) + visualSide * r * xFactor,
|
||||
y: (tarinai?.y || 0) + r * yFactor,
|
||||
angle: visualSide * angleFactor,
|
||||
distance: r * distFactor,
|
||||
offsetY: r * yFactor,
|
||||
};
|
||||
}
|
||||
const dx = Number.isFinite(opts.dx) ? opts.dx : 0;
|
||||
const dy = Number.isFinite(opts.dy) ? opts.dy : -1;
|
||||
const dist = Number.isFinite(opts.distance) ? opts.distance : Math.hypot(dx, dy);
|
||||
const clampFn = global.clamp || ((v, lo, hi) => Math.max(lo, Math.min(hi, v)));
|
||||
return {
|
||||
visualSide,
|
||||
x: (tarinai?.x || 0) + visualSide * r * 0.36,
|
||||
y: (tarinai?.y || 0) - r * 0.04,
|
||||
angle: Math.atan2(dy || -1, dx || visualSide),
|
||||
poseAngle: visualSide * 0.28,
|
||||
distance: clampFn(dist || r * 0.58, r * 0.20, r * 0.74),
|
||||
offsetY: clampFn(dy, -r * 0.74, r * 0.38),
|
||||
};
|
||||
}
|
||||
function applyTarinaiPose(item, tarinai, worldRef = null) {
|
||||
if (!item || !tarinai) return null;
|
||||
const anchor = tarinaiAnchor(item, tarinai);
|
||||
item.pinVisualSide = anchor.visualSide;
|
||||
item.x = anchor.x;
|
||||
item.y = anchor.y;
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.spin = isOshibyo(item) ? anchor.angle : (anchor.poseAngle ?? anchor.angle);
|
||||
item.vx = tarinai.vx || 0;
|
||||
item.vy = tarinai.vy || 0;
|
||||
item.deletable = false;
|
||||
if (worldRef) worldRef.drawListDirty = true;
|
||||
return anchor;
|
||||
}
|
||||
function ballAnchor(item, ball) {
|
||||
const angle = (ball?.spin || 0) + (item?.pinBallOffsetAngle || 0);
|
||||
const distance = Math.max(4, Number(item?.pinBallOffsetDistance || (ball?.r || 18) * 0.55) || (ball?.r || 18) * 0.55);
|
||||
return {
|
||||
angle,
|
||||
distance,
|
||||
visualSide: Math.cos(angle) >= 0 ? 1 : -1,
|
||||
x: (ball?.x || 0) + Math.cos(angle) * distance,
|
||||
y: (ball?.y || 0) + Math.sin(angle) * distance,
|
||||
spin: angle + (isOshibyo(item) ? 0.18 : Math.PI * 0.5),
|
||||
};
|
||||
}
|
||||
function applyBallPose(item, ball, worldRef = null) {
|
||||
if (!item || !ball) return null;
|
||||
const anchor = ballAnchor(item, ball);
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.x = anchor.x;
|
||||
item.y = anchor.y;
|
||||
item.vx = ball.vx || 0;
|
||||
item.vy = ball.vy || 0;
|
||||
item.pinVisualSide = anchor.visualSide;
|
||||
item.spin = anchor.spin;
|
||||
item.deletable = false;
|
||||
if (Number.isFinite(ball.spinVelocity)) item.spinVelocity = ball.spinVelocity;
|
||||
if (worldRef) worldRef.drawListDirty = true;
|
||||
return anchor;
|
||||
}
|
||||
function isAttachedToEntity(pin, entity) {
|
||||
if (!pin || !entity || !isPin(pin)) return false;
|
||||
if (entity.type === "ball") return pin.pinState === "ball_lodged" && pin.pinBallId === entity.id;
|
||||
const isTarinaiEntity = typeof global.Tarinai !== "undefined" && entity instanceof global.Tarinai;
|
||||
return isTarinaiEntity && pin.pinState === "lodged" && pin.pinTargetId === entity.id;
|
||||
}
|
||||
function drawAttachedForEntity(args = {}) {
|
||||
const { renderStack, entity, visibleRect, worldRef, time, drawnIds, isVisible } = args;
|
||||
if (!renderStack || !entity || !drawnIds || typeof isVisible !== "function") return 0;
|
||||
const source = entity.type === "ball" ? (renderStack.ballLodgedPins || []) : (renderStack.lodgedPins || []);
|
||||
let drawn = 0;
|
||||
for (const it of source) {
|
||||
if (!it || drawnIds.has(it.id) || !isAttachedToEntity(it, entity)) continue;
|
||||
if (isVisible(it, visibleRect)) it.draw?.(global.ctx || args.ctx, time ?? worldRef?.time ?? 0, args.lighting || null);
|
||||
drawnIds.add(it.id);
|
||||
drawn += 1;
|
||||
}
|
||||
return drawn;
|
||||
}
|
||||
global.TarinaiPinAttachmentSystem = Object.freeze({
|
||||
isPin,
|
||||
behaviorFor,
|
||||
isOshibyo,
|
||||
tarinaiVisualSide,
|
||||
tarinaiAnchor,
|
||||
applyTarinaiPose,
|
||||
ballAnchor,
|
||||
applyBallPose,
|
||||
isAttachedToEntity,
|
||||
drawAttachedForEntity,
|
||||
});
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
Loading…
Add table
Add a link
Reference in a new issue