/**
* @typedef AnimationOptions
* @property {number} dureeDeplacement
* @property {number} dureeAnimation
* @property {number} delai
*/
/**
* @typedef ImagePosition
* @property {number} x
* @property {number} y
* @property {number} direction
*/
/**
* Pour les objets mobilesCoords
* @param {Sokoban} sokoban
* @param {ImagePosition} position
* @param {AnimationOptions} animation
* @param {boolean} isAvatar
* @constructor
*/
function Mobile (sokoban, position, animation, isAvatar) {
const image = isAvatar ? sokoban.persoElt : sokoban.chatElt
if (!(image instanceof HTMLImageElement)) throw Error('sokoban non initialisé')
this.sokoban = sokoban
this.image = image
this.x = position.x
this.y = position.y
this.direction = position.direction
this.etat = -1// immobile
this.surplace = false
this.decalageY = position.decalageY
this.largeur = this.image.width / 4
this.hauteur = this.image.height / 4
this.animation = animation
}
Mobile.prototype.dessine = function (context) {
if (!this.image) throw Error('Aucune image à dessiner')
const c = this.sokoban.constantes
let frame = 0
// Numéro de l’image à prendre pour l’animation
let decalageX = 0
let decalageY = this.decalageY
// Décalage à appliquer à la position du personnage
if (this.etat >= this.animation.dureeDeplacement) {
// Si le déplacement a atteint ou dépassé le temps nécessaire pour s’effectuer, on le termine
this.etat = -1
} else if (this.etat >= 0) {
// On calcule l’image (frame) de l’animation à afficher
frame = Math.floor(this.etat / this.animation.dureeAnimation) % 4
// Nombre de pixels restant à parcourir entre les deux cases
const pixelsAParcourir = 48 - (48 * (this.etat / this.animation.dureeDeplacement))
// À partir de ce nombre, on définit le décalage en x et y.
if (this.direction === c.haut) {
decalageY = this.surplace ? 0 : pixelsAParcourir
} else if (this.direction === c.bas) {
decalageY = this.surplace ? 0 : -pixelsAParcourir
} else if (this.direction === c.gauche) {
decalageX = this.surplace ? 0 : pixelsAParcourir
} else if (this.direction === c.droite) {
decalageX = this.surplace ? 0 : -pixelsAParcourir
}
// On incrémente d’une frame
this.etat++
}
// @todo voir pourquoi ça plante de temps en temps avec firefox
// IndexSizeError: Index or size is negative or greater than the allowed amount
// cf https://app.bugsnag.com/sesamath/j3p/errors/616550934100b500078554ae
try {
context.drawImage(
this.image,
frame * this.largeur, // sx
this.direction * this.hauteur, // sy
this.largeur, // swidth
this.hauteur, // sheight
// Point de destination (dépend de la taille du personnage)
(this.x * 48) + decalageX + 8, // x
Math.max(((this.y + 1) * 48) - this.hauteur + decalageY, 0), // y
this.largeur,
this.hauteur
)
} catch (error) {
console.error(error)
}
}
Mobile.prototype.getCoordonneesAdjacentes = function (direction) {
const c = this.sokoban.constantes
const coord = { x: this.x, y: this.y }
switch (direction) {
case c.bas :
coord.y++
break
case c.gauche:
coord.x--
break
case c.droite :
coord.x++
break
case c.haut :
coord.y--
break
}
return coord
}
/**
*
* @param direction
* @param {Sokoban} sokoban
* @return {boolean}
*/
Mobile.prototype.deplacer = function (direction, sokoban) {
// On ne peut pas se déplacer si un mouvement est déjà en cours !
if (this.etat >= 0) {
return false
}
// On change la direction du personnage
this.direction = direction
// On vérifie que la case demandée est bien située dans la carte
const prochaineCase = this.getCoordonneesAdjacentes(direction)
if (!sokoban.libre(prochaineCase.x, prochaineCase.y, direction)) {
// if (prochaineCase.x < 0 || prochaineCase.y < 0 || prochaineCase.x >= 10 || prochaineCase.y >= 10) {
this.surplace = true
this.etat = 1
// pour bouger sur place
return false
}
this.surplace = false
// On commence l’animation
this.etat = 1
sokoban.scene[this.y][this.x] = '-'
// On effectue le déplacement
this.x = prochaineCase.x
this.y = prochaineCase.y
if (sokoban.correction()) {
sokoban.feedbackElt.innerText = 'Bravo !'
const hasNext = (sokoban.tableauCourant + 1) < sokoban.tableaux.length
if (hasNext) {
if (sokoban.tableauMax < sokoban.tableauCourant + 1) sokoban.tableauMax = sokoban.tableauCourant + 1
if (sokoban.nextBtn) sokoban.resetNav()
// sinon on passe au suivant d’office
else setTimeout(sokoban.change.bind(sokoban, true), 3000)
}
}
return true
}
export default Mobile