/**
* @license MIT
* Copyright © 2026 Steve Butler (henspace.com)
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the “Software”),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/**
* @module hcje/collisions
* @description
* Module providing support for sprite collisions.
*/
import * as utils from './utils.js';
/**
* Function that is called when a collision has been detected.
* @callback notifyCollision
* @param {module:hcje/sprites.Sprite} masterSprite - the sprite that detected the collision.
* @param {module:hcje/sprites.Sprite} otherSprite - the other sprite involved in the collision.
*/
/**
* Object involved in collisions.
* @typedef {Object} Collider
* @property {module:hcje/sprites.Sprite} sprite - the sprite that is colliding.
* @property {module:hcje/utils~Shrinkage} [shrinkage] - shrinkage to apply to derive collision rectangle.
* @property {module:hcje/collisions~notifyCollision} [callback] - function to call when collision is detected.
*/
/**
* Class to manage collisions. Instances can be added to an {@link module:hcje/sprites.Sprite}.
* @implements module:hcje/sprites~AnimationTarget
*/
export class CollisionDetector {
/**
* Controlling sprite.
* @type {module:hcje/collisions~Collider}
*/
#master;
/**
* Collision targets. Note they are keyed by the Sprite.
* @type {Map<module:hcje/sprites.Sprite, module:hcje/collisions~Collider>}
*/
#targets
/**
* Construct the collider.
* @param {module:hcje/collisions~Collider} masterCollider - main collider which tests for collisions.
*/
constructor(masterCollider) {
this.#master = masterCollider;
this.#targets = new Map();
}
/**
* Add target. The target is added to a Map using the target's sprite property as a key. As such, each sprite can
* only occur once even if a different callback is used.
* @param {module:hcje/collisions~Collider} targetCollider - the collision target which will be checked when
* collisions are detected.
*/
addTarget(targetCollider) {
this.#targets.set(targetCollider.sprite, targetCollider);
}
/**
* Remove a target sprite. Note that targets are keyed by the sprite, so only the sprite object is required to
* remove it.
* @param {module:hcje/sprites~Sprite} sprite - the sprite to remove.
*/
removeTargetSprite(sprite) {
this.#targets.delete(sprite);
}
/**
* Look for collisions. Note that if a sprite is marked as killed, it is removed from the list of targets. Likewise
* no collisions are detected if the controlling sprite has been killed. In that case all targets are removed from
* its map.
*/
detect() {
if (this.#targets.size === 0) {
return;
} else if (this.#master.sprite.isKilled() ) {
this.#targets.clear();
return;
}
this.#targets.forEach((target) => {
if (target.sprite.isKilled()) {
this.#targets.delete(target.sprite);
} else if (utils.areRectsOverlapping(utils.shrinkRect(this.#master.sprite.bounds, this.#master.shrinkage),
utils.shrinkRect(target.sprite.bounds, target.shrinkage))) {
this.#master.callback?.(this.#master.sprite, target.sprite);
target.callback?.(this.#master.sprite, target.sprite);
}
});
}
}