diff --git a/static/js/play/__tests__/collision-rules-controller.test.js b/static/js/play/__tests__/collision-rules-controller.test.js index b3d64dc1..114efed5 100644 --- a/static/js/play/__tests__/collision-rules-controller.test.js +++ b/static/js/play/__tests__/collision-rules-controller.test.js @@ -49,7 +49,7 @@ function rect(left, top, width, height) { return { left, top, width, height, right: left + width, bottom: top + height, shape: 'rectangle' }; } -test('forgeCollisionRuleTriggerActive — trigger "collision" : vrai seulement si les boîtes se chevauchent', () => { +test('forgeCollisionRuleTriggerActive — trigger "collision" : vrai si les boîtes se chevauchent, faux si loin', () => { const objRect = rect(0, 0, 20, 20); const overlapping = rect(10, 10, 20, 20); const far = rect(200, 200, 20, 20); @@ -57,6 +57,18 @@ test('forgeCollisionRuleTriggerActive — trigger "collision" : vrai seulement s assert.equal(forgeCollisionRuleTriggerActive({ trigger: 'collision' }, objRect, far), false); }); +test('forgeCollisionRuleTriggerActive — trigger "collision" : vrai aussi pour un simple CONTACT (bords qui se touchent sans se chevaucher)', () => { + // Bug corrigé : cet objet est presque toujours aussi un obstacle + // SOLIDE (forgeWouldCollide, personnage-controller.js) — le joueur ne + // peut donc jamais dépasser un contact tangent, et un chevauchement + // STRICT n'était donc jamais atteint : "à la collision" ne se + // déclenchait jamais en jeu réel. objRect va de x=[20,40], playerRect + // juste à côté en x=[0,20] : bord à bord, AUCUN chevauchement géométrique. + const objRect = rect(20, 0, 20, 20); + const touchingPlayer = rect(0, 0, 20, 20); + assert.equal(forgeCollisionRuleTriggerActive({ trigger: 'collision' }, objRect, touchingPlayer), true); +}); + test('forgeCollisionRuleTriggerActive — trigger "perimetre" : distance entre centres <= perimetre_px', () => { const objRect = rect(0, 0, 10, 10); // centre (5,5) const near = rect(10, 0, 10, 10); // centre (15,5) -> distance 10 diff --git a/static/js/play/collision-rules-controller.js b/static/js/play/collision-rules-controller.js index c5c1c1f8..beba63a4 100644 --- a/static/js/play/collision-rules-controller.js +++ b/static/js/play/collision-rules-controller.js @@ -32,13 +32,35 @@ function forgeElementBoxRect(el, collision) { return forgeCollisionRectFromBox({ left: left, top: top, width: width, height: height }, collision); } -// "collision" : même chevauchement de boîtes que forgeWouldCollide -// (personnage-controller.js). "perimetre" : distance entre les CENTRES -// des deux boîtes <= rule.perimetre_px (pas entre les bords — plus simple -// et suffisant pour une portée approximative, cohérent avec le reste du -// moteur qui ne vise pas une physique précise). +// Une petite marge (px) appliquée SEULEMENT à la détection du +// déclencheur "collision" ci-dessous — jamais au blocage physique du +// déplacement (forgeWouldCollide, personnage-controller.js, qui reste +// strict). Bug corrigé : un objet est presque toujours aussi un +// OBSTACLE SOLIDE (collision activée par défaut) — le joueur ne peut +// donc jamais aller au-delà d'un contact tangent (bords qui se touchent +// SANS se chevaucher), et un chevauchement STRICT (forgeShapesOverlap +// nu) n'était donc jamais atteint : la règle "à la collision" ne se +// déclenchait jamais. Cette marge élargit la boîte testée pour que ce +// simple contact compte comme une collision. +var FORGE_COLLISION_RULE_TOUCH_MARGIN = 3; + +function forgeExpandRect(rect, margin) { + return { + left: rect.left - margin, top: rect.top - margin, + right: rect.right + margin, bottom: rect.bottom + margin, + width: rect.width + margin * 2, height: rect.height + margin * 2, + shape: rect.shape, + }; +} + +// "collision" : chevauchement des boîtes, ÉLARGI d'une petite marge pour +// détecter un simple contact (voir FORGE_COLLISION_RULE_TOUCH_MARGIN +// ci-dessus). "perimetre" : distance entre les CENTRES des deux boîtes +// <= rule.perimetre_px (pas entre les bords — plus simple et suffisant +// pour une portée approximative, cohérent avec le reste du moteur qui ne +// vise pas une physique précise). function forgeCollisionRuleTriggerActive(rule, objRect, playerRect) { - if (rule.trigger === 'collision') return forgeShapesOverlap(objRect, playerRect); + if (rule.trigger === 'collision') return forgeShapesOverlap(forgeExpandRect(objRect, FORGE_COLLISION_RULE_TOUCH_MARGIN), playerRect); var ocx = objRect.left + objRect.width / 2, ocy = objRect.top + objRect.height / 2; var pcx = playerRect.left + playerRect.width / 2, pcy = playerRect.top + playerRect.height / 2; var dist = Math.hypot(ocx - pcx, ocy - pcy); @@ -179,7 +201,7 @@ if (typeof module !== 'undefined' && module.exports) { module.exports = { forgeCollisionRuleTriggerActive, forgeRunCollisionRuleAction, forgeCollisionRuleTick, forgeStartCollisionRuleControllers, forgeStopCollisionRuleControllers, - forgeCollisionRuleStateKey, forgeElementBoxRect, + forgeCollisionRuleStateKey, forgeElementBoxRect, forgeExpandRect, }; const conditionsMod = require('./conditions.js'); global.forgeCollisionRectFromBox = global.forgeCollisionRectFromBox || conditionsMod.forgeCollisionRectFromBox;