Fix bulle "Appuie sur X" trop haute au-dessus du personnage
La bulle s'ancrait sur le cadre BRUT du sprite (<img> style.left/top/ width/height) — beaucoup de sprites (animaux CraftPix notamment, voir screens/labels/animal_sprite_library.py) ont un très grand canevas padé autour d'une silhouette bien plus petite, faisant flotter la bulle loin au-dessus du personnage visible. Elle s'ancre maintenant sur la BOÎTE DE COLLISION (objRect, déjà résolue via forgeElementBoxRect — largeur/hauteur/décalage réglables dans "🧱 Collision"), que l'auteur ajuste déjà pour épouser la silhouette réelle : ancre bien plus fidèle, sans configuration supplémentaire à faire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
00f9cf33e9
commit
aa17614c7b
@@ -136,6 +136,38 @@ test('tick — la collision entre le joueur et un ennemi déclenche "attaque" un
|
|||||||
} finally { global.document = previousDoc; }
|
} finally { global.document = previousDoc; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('tick — la bulle "Appuie sur X" s\'ancre sur la BOÎTE DE COLLISION, pas le cadre brut du sprite', () => {
|
||||||
|
// Bug corrigé ("c'est trop haut") : un sprite très padé (animaux
|
||||||
|
// CraftPix...) a un cadre bien plus grand que sa silhouette visible —
|
||||||
|
// ancrer la bulle sur ce cadre brut la faisait flotter loin au-dessus
|
||||||
|
// du personnage. objRect (issu de forgeElementBoxRect, qui applique
|
||||||
|
// déjà collision.width/height/offset_x/offset_y) doit être utilisé.
|
||||||
|
const player = { id: 1, kind: 'personnage', personnage_role: 'joueur' };
|
||||||
|
const pnj = {
|
||||||
|
id: 3, kind: 'personnage',
|
||||||
|
// Sprite de 100×200 px, mais silhouette réelle 20×20 centrée en bas
|
||||||
|
// (offset_y positif = décalée vers le bas, voir collision_settings.py).
|
||||||
|
collision: { enabled: true, shape: 'rectangle', width: 20, height: 20, offset_x: 0, offset_y: 80 },
|
||||||
|
collision_rules: [{ trigger: 'collision', action: { type: 'interagir', sub_action: { type: 'quete', quete_id: 1 } } }],
|
||||||
|
};
|
||||||
|
const parent = fakeParentEl();
|
||||||
|
const playerEl = fakeEl(parent);
|
||||||
|
playerEl.style = { left: '45px', top: '175px', width: '20px', height: '20px' }; // chevauche la silhouette réelle du pnj (40,170)-(60,190)
|
||||||
|
const pnjEl = fakeEl(parent);
|
||||||
|
pnjEl.style = { left: '0px', top: '0px', width: '100px', height: '200px' }; // grand cadre padé
|
||||||
|
|
||||||
|
const previousDoc = global.document;
|
||||||
|
global.gameData = { personnage_animations: {} };
|
||||||
|
try {
|
||||||
|
global.document = fakeDocument({ 1: playerEl, 3: pnjEl });
|
||||||
|
forgeCollisionRuleTick({ elements: [player, pnj] });
|
||||||
|
const bubble = parent.querySelector('.collisionInteractBubble[data-for-object-id="3"]');
|
||||||
|
// Boîte de collision : left = (100-20)/2+0 = 40, top = (200-20)/2+80 = 170.
|
||||||
|
assert.equal(bubble.style.top, '170px'); // PAS "0px" (le haut du cadre brut)
|
||||||
|
assert.equal(bubble.style.left, '50px'); // centrée sur la boîte de collision (40 + 20/2)
|
||||||
|
} finally { global.document = previousDoc; }
|
||||||
|
});
|
||||||
|
|
||||||
test('tick — "interagir" affiche la bulle "Appuie sur [touche]" pendant la collision, puis la cache en sortant', () => {
|
test('tick — "interagir" affiche la bulle "Appuie sur [touche]" pendant la collision, puis la cache en sortant', () => {
|
||||||
const player = { id: 1, kind: 'personnage', personnage_role: 'joueur', personnage_commandes: { interagir: 'e' } };
|
const player = { id: 1, kind: 'personnage', personnage_role: 'joueur', personnage_commandes: { interagir: 'e' } };
|
||||||
const pnj = { id: 3, kind: 'personnage', collision_rules: [{ trigger: 'collision', action: { type: 'interagir', sub_action: { type: 'quete', quete_id: 'q-2' } } }] };
|
const pnj = { id: 3, kind: 'personnage', collision_rules: [{ trigger: 'collision', action: { type: 'interagir', sub_action: { type: 'quete', quete_id: 'q-2' } } }] };
|
||||||
|
|||||||
@@ -93,17 +93,23 @@ function forgeCollisionInteractBubbleEl(objectId, parentEl) {
|
|||||||
return bubble;
|
return bubble;
|
||||||
}
|
}
|
||||||
|
|
||||||
function forgeShowCollisionInteractBubble(objectEl, objectId, key) {
|
// Positionnée sur la BOÎTE DE COLLISION (objRect, déjà résolue via
|
||||||
|
// forgeElementBoxRect — largeur/hauteur/décalage réglables dans "🧱
|
||||||
|
// Collision") plutôt que sur le cadre brut du <img> : beaucoup de
|
||||||
|
// sprites (notamment les animaux CraftPix, voir screens/labels/
|
||||||
|
// animal_sprite_library.py) ont un TRÈS GRAND canevas très padé autour
|
||||||
|
// d'une silhouette bien plus petite — ancrée sur le cadre brut, la
|
||||||
|
// bulle flottait loin au-dessus du personnage visible (bug corrigé :
|
||||||
|
// "c'est trop haut"). La boîte de collision, que l'auteur ajuste déjà
|
||||||
|
// pour épouser la silhouette réelle, donne une ancre bien plus fidèle.
|
||||||
|
function forgeShowCollisionInteractBubble(objectEl, objectId, objRect, key) {
|
||||||
var parentEl = objectEl.parentElement;
|
var parentEl = objectEl.parentElement;
|
||||||
if (!parentEl) return;
|
if (!parentEl) return;
|
||||||
var bubble = forgeCollisionInteractBubbleEl(objectId, parentEl);
|
var bubble = forgeCollisionInteractBubbleEl(objectId, parentEl);
|
||||||
bubble.textContent = 'Appuie sur ' + key;
|
bubble.textContent = 'Appuie sur ' + key;
|
||||||
bubble.style.display = 'block';
|
bubble.style.display = 'block';
|
||||||
var left = parseFloat(objectEl.style.left) || 0;
|
bubble.style.left = (objRect.left + objRect.width / 2) + 'px'; // centrée horizontalement (voir transform:translateX(-50%), static/style.css)
|
||||||
var top = parseFloat(objectEl.style.top) || 0;
|
bubble.style.top = objRect.top + 'px'; // AU-DESSUS de la boîte de collision (voir transform:translateY(-100%), static/style.css)
|
||||||
var width = parseFloat(objectEl.style.width) || 0;
|
|
||||||
bubble.style.left = (left + width / 2) + 'px'; // centrée horizontalement (voir transform:translateX(-50%), static/style.css)
|
|
||||||
bubble.style.top = top + 'px'; // AU-DESSUS de l'objet (voir transform:translateY(-100%), static/style.css)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function forgeHideCollisionInteractBubble(objectId, objectEl) {
|
function forgeHideCollisionInteractBubble(objectId, objectEl) {
|
||||||
@@ -158,7 +164,7 @@ function forgeCollisionRuleTick(screenData) {
|
|||||||
if (rule.action.type === 'interagir') {
|
if (rule.action.type === 'interagir') {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
anyInteractActive = true;
|
anyInteractActive = true;
|
||||||
forgeShowCollisionInteractBubble(targetEl, obj.id, (player.personnage_commandes && player.personnage_commandes.interagir) || 'E');
|
forgeShowCollisionInteractBubble(targetEl, obj.id, objRect, (player.personnage_commandes && player.personnage_commandes.interagir) || 'E');
|
||||||
}
|
}
|
||||||
forgeCollisionRuleActiveState[stateKey] = isActive;
|
forgeCollisionRuleActiveState[stateKey] = isActive;
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user