//============================================================================= // Sphinx-Minimap.js //============================================================================= /*: * @plugindesc Minimap affichée à l'écran * @author Sphinx * * @help * //========================================================================== * // Plugin : Sphinx-Minimap * // Date : 21 mars 2020 * // Auteur : Sphinx * //========================================================================== * Affiche une minimap sur laquelle sont notés la position du joueur et des * évènements marqués. * La couleur du point symbolisant le joueur est un paramètre de ce plugin. * Sont autorisés tous les codes couleurs CSS, tel que le format hexadécimal * (#xxx ou #xxxxxx), ou encore des couleurs nommés (voir la liste à cette * adresse : https://developer.mozilla.org/fr/docs/Web/CSS/Type_color). * * Pour marquer un évènement de la carte sur la minimap, il faut insérer le * commentaire suivant quelque part dans la page de l'event : * Minimap:COULEUR * Où COULEUR est une couleur CSS valide. * * Vous pouvez décider au moyen d'un paramètre booléen de ce plugin si la * minimap doit s'afficher par défaut ou pas. Dans les deux cas, il est * toujours possible soit de l'activer pour certaines cartes (si elle est * désactivée par défaut) soit de la désactiver pour certaines cartes (si elle * est activée par défaut). Pour se faire, voici les balises meta qu'il faut * renseigner sur vos cartes : * - <minimap> : affiche la minimap * - <no-minimap> : masque la minimap * * Il est possible de paramétrer d'autres réglages pour la minimap. Par défaut, * elle s'affichera entièrement ainsi que tous les events à marquer. * * Pour n'afficher qu'une partie de la minimap (utile pour les grosses cartes), * vous pouvez régler la taille de la minimap avec la balise meta suivante : * - <minimap-size:LARGEUR HAUTEUR> * où LARGEUR et HAUTEUR sont les dimensions de la minimap exprimées en tiles. * * Pour régler le niveau d'informations affichées au joueur quand il vient * d'arriver sur la map, utilisez l'une des balises meta suivantes : * - <minimap-discovered:map> : le joueur voit tout le niveau (les events * mais aussi tous les détails de la carte). * C'est l'option par défaut. * - <minimap-discovered:events> : le joueur ne voit que la position des * events affichés sur la carte, mais pas * la structure de la carte. * - <minimap-discovered:nothing> : le joueur ne voit aucune info sur ce * qui l'entoure, jusqu'à ce qu'il explore * la carte. * * NB : Si vous utilisez en même temps la balise meta minimap-size et * minimap-discovered, peu importe le niveau de détail affiché au joueur, il ne * verra rien de plus que ce que la balise meta minimap-size permet * * Dépendances : * Sphinx-Polyfill * * @param defaultDisplay * @text Affichage par défaut de la minimap * @desc Définit si la minimap doit être affichée par défaut ou non. * @type boolean * @on Afficher par défaut * @off Masquer par défaut * @default false * * @param tileSize * @text Taille d'un tile sur la minimap * @desc Dimensions (largeur et hauteur) de chaque case sur la minimap. * @type number * @min 4 * @max 16 * @default 8 * * @param playerColor * @text Couleur du point symbolisant le joueur dans la minimap. * @desc Sont autorisés les codes couleur hexadécimaux à 3 ou 6 caractères ou encore les couleurs nommées CSS. * @type text * @default darkgreen * * @param groundColor * @text Couleur du sol (et de tout ce qui est praticable d'une manière générale) * @desc Sont autorisés les codes couleur hexadécimaux à 3 ou 6 caractères ou encore les couleurs nommées CSS. * @type text * @default gray * * @param wallColor * @text Couleur du mur (et de tout ce qui n'est pas praticable d'une manière générale) * @desc Sont autorisés les codes couleur hexadécimaux à 3 ou 6 caractères ou encore les couleurs nommées CSS. * @type text * @default black * * @param alpha * @text Transparence de la minimap * @desc Un nombre décimal compris entre 0 (transparence totale) et 1 (opacité totale). * @type number * @decimals 2 * @min 0 * @max 1 * @default 0.33 */ (function() { // Regexp complète pour extraire la couleur de l'event sur la minimap (couleurs au format hexadécimal ou l'une des couleurs CSS reconnues jusqu'à la norme CSS4) REGEXP_CSS_COLOR = /Minimap\s*:\s*(\#[0-9a-f]{6}|\#[0-9a-f]{3}|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua|orange|aliceblue|antiquewhite|aquamarine|azure|beige|bisque|blanchedalmond|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|gainsboro|ghostwhite|gold|goldenrod|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|limegreen|linen|magenta|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|oldlace|olivedrab|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|thistle|tomato|turquoise|violet|wheat|whitesmoke|yellowgreen|rebeccapurple)/i; // Paramètres du plugin PARAMS = PluginManager.parameters("Sphinx-Minimap"); DEFAULT_DISPLAY = PARAMS["defaultDisplay"] === "true"; // Affichage par défaut de la minimap ALPHA = parseFloat(PARAMS["alpha"]); // Transparence du fond (murs & sols) de la minimap TILE_SIZE = parseInt(PARAMS["tileSize"], 10); // Taille en pixels de chaque carreau sur la minimap PLAYER_COLOR = getColorToRGBA(PARAMS["playerColor"]); // Couleur du repère désignant le joueur GROUND_COLOR = getColorToRGBA(PARAMS["groundColor"], ALPHA); // Couleur du sol (et de tout ce qui est praticable) WALL_COLOR = getColorToRGBA(PARAMS["wallColor"], ALPHA); // Couleur du mur (et de tout ce qui n'est pas praticable) /* * Scene_Map */ // Initialisation de la minimap dans Scene_Map Scene_Map.prototype.sphinxMaplinkCreateDisplayObjects = Scene_Map.prototype.createDisplayObjects; Scene_Map.prototype.createDisplayObjects = function() { Scene_Map.prototype.sphinxMaplinkCreateDisplayObjects.call(this); if($gameMap.displayMinimap()) { this.initializeMinimap(); $gameMap.discoverMap($gamePlayer.y, $gamePlayer.x); } }; // Création de la minimap et ajout à la scène Scene_Map.prototype.initializeMinimap = function() { this.minimap = new Sphinx_Minimap(); this.addChildAt(this.minimap, 1); }; // Mise à jour de la minimap Scene_Map.prototype.sphinxMinimapUpdate = Scene_Map.prototype.update; Scene_Map.prototype.update = function() { if($gameMap.displayMinimap()) { this.minimap.drawMinimap(); } Scene_Map.prototype.sphinxMinimapUpdate.call(this); }; /* * Game_Player */ Game_Player.prototype.sphinxMinimapIncreaseSteps = Game_Player.prototype.increaseSteps; Game_Player.prototype.increaseSteps = function() { Game_Player.prototype.sphinxMinimapIncreaseSteps.call(this); if($gameMap.displayMinimap()) { $gameMap.discoverMap($gamePlayer.y, $gamePlayer.x); } }; /* * Game_Map */ // Initialise les données de la minimap Game_Map.prototype.sphinxMinimapSetup = Game_Map.prototype.setup; Game_Map.prototype.setup = function(mapId) { Game_Map.prototype.sphinxMinimapSetup.call(this, mapId); this.initializeMinimap(); } // Détermine si la minimap doit être affichée ou non Game_Map.prototype.displayMinimap = function() { return (DEFAULT_DISPLAY && !$dataMap.meta["no-minimap"]) || $dataMap.meta["minimap"]; } // Calcul de la carte des colisions de la map courante Game_Map.prototype.initializeColliderMap = function() { this.colliderMap = []; for(var row = 0; row < $dataMap.height; ++row) { this.colliderMap[row] = []; for(var col = 0; col < $dataMap.width; ++col) { this.colliderMap[row][col] = [] for(var dir = 2; dir < 10; dir += 2) { this.colliderMap[row][col][dir / 2 - 1] = this.isPassable(col, row, dir); } } } } // Initialise les informations relatives à la minimap Game_Map.prototype.initializeMinimap = function() { this.minimapDisplayedWidth = $dataMap.width; this.minimapDisplayedHeight = $dataMap.height; if($dataMap.meta["minimap-size"] !== undefined) { displayedSize = $dataMap.meta["minimap-size"].trim().split(" ").map(value => parseInt(value, 10)); this.minimapDisplayedWidth = displayedSize[0]; this.minimapDisplayedHeight = displayedSize[1]; } this.initializeColliderMap(); this.initializeDiscoveredMap(); } // Initialise la découverte de la map Game_Map.prototype.initializeDiscoveredMap = function() { // Initialisation des données this.discoveredMap = []; events = false; structure = false; bubbled = false; // Niveau de détails affichés switch($dataMap.meta["minimap-discovered"]) { // Nothing : rien n'est montré au joueur case "nothing": break; // Events : la position des évents est affichée, mais ni les murs ni le sol case "events": events = true; break; // Map : tout est affiché pour l'ensemble de la carte case "map": default: events = true; structure = true; bubbled = true; break; }; // Pour chaque tile, enregistrement du niveau de détails affichés for(var row = 0; row < $dataMap.height; ++row) { this.discoveredMap[row] = []; for(var col = 0; col < $dataMap.width; ++col) { this.discoveredMap[row][col] = { events: events, structure: structure, bubbled: bubbled }; } } 1 }; // Affiche les informations découvertes sur la minimap Game_Map.prototype.discoverMap = function(startRow, startCol) { // Si la case a déjà engendré une propagation, on sort if(this.discoveredMap[startRow][startCol].bubbled) return; // Initialisation de la liste des coordonnées - on part des coordonnées du joueur var coords = [ startRow + ";" + startCol ]; // Parcours de la liste des coordonées de la salle for(var i = 0; i < coords.length; ++i) { // Extraction des coordonnées var coord = coords[i].split(";").map(value => parseInt(value, 10)); var row = coord[0]; var col = coord[1]; // Exploration dans toutes les directions for(var d = 0; d < 4; ++d) { // Initialisation des coordonnées explorées var r = row; var c = col; // Calcul de la modification de coordonnées en fonction de la direction var x = 0; var y = 0; switch(d) { // Bas case 0: y = 1; break; // Gauche case 1: x = -1; break; // Droite case 2: x = 1; break; // Haut case 3: y = -1; break; } // Tant que les cases sont passables dans cette direction (et la case d'en face dans la direction opposée soit 3 - d) et qu'elles sont reliées à plus de 2 cases sur les 8 autour while(this.colliderMap[r][c][d] && this.colliderMap[r + y][c + x][3 - d] && this.getPassableCount(r, c) > 2) { // On avance d'une case dans cette direction r += y; c += x; // Si elle est reliée à plus de 2 cases sur les 8 autour if(!coords.includes(r + ";" + c) && this.getPassableCount(r, c) > 2) { // On ajoute les coordonnées de la nouvelle case (on les empacte dans une chaine de caractères pour pouvoir tester si elles existent dans le tableau des coordonnées analysées) coords.push(r + ";" + c); } } } } for(i = 0; i < coords.length; ++i) { // Extraction des coordonnées var coord = coords[i].split(";").map(value => parseInt(value, 10)); var row = coord[0]; var col = coord[1]; // Découverte de la case, et des cases avoisinantes (de [row - 3;col - 1] à [row + 1;col + 1]) this.discoverTile(row, col); } }; // Retourne true si les coordonnées sont en dehors de la carte Game_Map.prototype.isInvalidCoords = function(row, col) { return row < 0 || row >= $dataMap.height || col < 0 || col >= $dataMap.width; } // Retourne le nombre d'accès pour la case passée en paramètres Game_Map.prototype.getPassableCount = function(row, col) { // Vérification de la propagation : si 3 des cases autour du tile courant sont praticables, alors on active if(this.isInvalidCoords(row, col)) return; // la propagation autour de cette case. var count = 0; // Vers le bas if(row < $dataMap.height - 1 && this.colliderMap[row][col][0] && this.colliderMap[row + 1][col][3]) ++count; // Vers la gauche if(col > 0 && this.colliderMap[row][col][1] && this.colliderMap[row][col - 1][2]) ++count; // Vers la droite if(col < $dataMap.width - 1 && this.colliderMap[row][col][2] && this.colliderMap[row][col + 1][1]) ++count; // Vers le haut if(row > 0 && this.colliderMap[row][col][3] && this.colliderMap[row - 1][col][0]) ++count; // Vers le bas gauche if( row < $dataMap.height - 1 && col > 0 && this.colliderMap[row + 1][col - 1][3] && this.colliderMap[row][col - 1][0] && this.colliderMap[row + 1][col - 1][1] && this.colliderMap[row + 1][col][2] ) ++count; // Vers le haut gauche if( row > 0 && col > 0 && this.colliderMap[row - 1][col - 1][0] && this.colliderMap[row][col - 1][3] && this.colliderMap[row - 1][col - 1][1] && this.colliderMap[row - 1][col][2] ) ++count; // Vers le haut droite if( row > 0 && col < $dataMap.width - 1 && this.colliderMap[row - 1][col + 1][0] && this.colliderMap[row][col + 1][3] && this.colliderMap[row - 1][col + 1][2] && this.colliderMap[row - 1][col][1] ) ++count; // Vers le bas droite if( row < $dataMap.height - 1 && col < $dataMap.width - 1 && this.colliderMap[row + 1][col + 1][3] && this.colliderMap[row][col + 1][0] && this.colliderMap[row + 1][col + 1][2] && this.colliderMap[row + 1][col][1] ) ++count; return count; } // Découvre un tile Game_Map.prototype.discoverTile = function(row, col) { // Elimination des recherches hors de la map if(this.isInvalidCoords(row, col)) return; // Elimination des recherches déjà effectuées if(this.discoveredMap[row][col].bubbled) return; // Affiche la case sur la minimap (event et sol) et propage sur une zone allant de [row - 3;col - 1] à [row + 1;col + 1] for(r = row - 3; r <= row + 1; ++r) { for(c = col - 3; c <= col + 1; ++c) { if(this.isInvalidCoords(r, c) || this.discoveredMap[r][c].structure) continue; this.discoveredMap[r][c].events = true; this.discoveredMap[r][c].structure = true; } } // La case est marquée propagée this.discoveredMap[row][col].bubbled = true; }; // Met à jour les limites de la minimap (en cas d'affichage partiel) Game_Map.prototype.updateMinimapLimits = function() { this.firstLineIndex = Math.min(Math.max(0, $gamePlayer.y - this.minimapDisplayedHeight / 2), $dataMap.height - this.minimapDisplayedHeight); this.firstColIndex = Math.min(Math.max(0, $gamePlayer.x - this.minimapDisplayedWidth / 2), $dataMap.width - this.minimapDisplayedWidth); }; /* * Sphinx_Minimap */ function Sphinx_Minimap() { this.initialize.apply(this, arguments); }; Sphinx_Minimap.prototype = Object.create(Sprite_Base.prototype); Sphinx_Minimap.prototype.constructor = Sphinx_Minimap; // Initialisation de la minimap Sphinx_Minimap.prototype.initialize = function() { Sprite_Base.prototype.initialize.call(this); // $gameMap.initializeMinimap(); this.x = 12; this.y = Graphics.height - $gameMap.minimapDisplayedHeight * TILE_SIZE - 12; this.bitmap = new Bitmap($gameMap.minimapDisplayedWidth * TILE_SIZE, $gameMap.minimapDisplayedHeight * TILE_SIZE); this.context = this.bitmap._context; }; // Dessin de la minimap Sphinx_Minimap.prototype.drawMinimap = function() { // Fond de la minimap this.bitmap.clear(); this.context.fillStyle = WALL_COLOR; this.context.fillRect(0, 0, $gameMap.minimapDisplayedWidth * TILE_SIZE, $gameMap.minimapDisplayedHeight * TILE_SIZE); $gameMap.updateMinimapLimits(); // Dessine la portion de la minimap à afficher for(var row = 0; row < $dataMap.height; ++row) { for(var col = 0; col < $dataMap.width; ++col) { this.drawMinimapTile(row, col); } } // Dessin des éléments affichés sur la minimap this.markPlayer(); this.markEvents(); }; // Marque la position du joueur sur la minimap Sphinx_Minimap.prototype.markPlayer = function() { this.context.fillStyle = PLAYER_COLOR; this.drawMark($gamePlayer.y, $gamePlayer.x); }; // Marque la position de tous les évènements sur la minimap Sphinx_Minimap.prototype.markEvents = function() { for(var eventId = 0; eventId < $gameMap.events().length; ++eventId) { var event = $gameMap.events()[eventId]; if(event._erased) continue; if($gameMap.discoveredMap[event.y][event.x].events) { var commands = event.page().list for(var commandId = 0; commandId < commands.length; ++commandId) { var command = commands[commandId]; if(command.code != 108 && command.code != 408) continue; if(command.parameters[0].match(REGEXP_CSS_COLOR)) { var color = command.parameters[0].match(REGEXP_CSS_COLOR)[1]; this.context.fillStyle = color; this.drawMark(event.y, event.x); } } } } }; // Dessine une marque circulaire sur la minimap Sphinx_Minimap.prototype.drawMark = function(row, col) { this.context.beginPath(); this.context.ellipse((col - $gameMap.firstColIndex) * TILE_SIZE + TILE_SIZE / 2, (row - $gameMap.firstLineIndex) * TILE_SIZE + TILE_SIZE / 2, TILE_SIZE / 3, TILE_SIZE / 3, 0, 0, 2 * Math.PI); this.context.fill(); }; // Dessine un tile de la minimap Sphinx_Minimap.prototype.drawMinimapTile = function(row, col) { // Franchissement impossible dans les 4 directions if(!$gameMap.discoveredMap[row][col].structure || (!$gameMap.colliderMap[row][col][0] && !$gameMap.colliderMap[row][col][1] && !$gameMap.colliderMap[row][col][2] && !$gameMap.colliderMap[row][col][3])) { // Tile infranchissable ou non découvert // this.context.fillStyle = WALL_COLOR; // this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE, TILE_SIZE); } else { // Dessin du sol praticable this.context.fillStyle = GROUND_COLOR; this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE, TILE_SIZE); // Dessins d'éventuels murs this.context.fillStyle = WALL_COLOR; // Franchissement impossible vers le bas if(!$gameMap.colliderMap[row][col][0]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, TILE_SIZE, TILE_SIZE / 4); } // Franchissement impossible vers la gauche if(!$gameMap.colliderMap[row][col][1]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE / 4, TILE_SIZE); } // Franchissement impossible vers la droite if(!$gameMap.colliderMap[row][col][2]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE / 4, TILE_SIZE); } // Franchissement impossible vers le haut if(!$gameMap.colliderMap[row][col][3]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE, TILE_SIZE / 4); } // Jonction coin haut gauche if(col > 0 && !$gameMap.colliderMap[row][col - 1][3] && row > 0 && !$gameMap.colliderMap[row - 1][col][1]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE / 4, TILE_SIZE / 4); } // Jonction coin haut droite if(col < $dataMap.width - 1 && !$gameMap.colliderMap[row][col + 1][3] && row > 0 && !$gameMap.colliderMap[row - 1][col][2]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, (row - $gameMap.firstLineIndex) * TILE_SIZE, TILE_SIZE / 4, TILE_SIZE / 4); } // Jonction coin bas gauche if(col > 0 && !$gameMap.colliderMap[row][col - 1][0] && row < $dataMap.height - 1 && !$gameMap.colliderMap[row + 1][col][1]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE, (row - $gameMap.firstLineIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, TILE_SIZE / 4, TILE_SIZE / 4); }1 // Jonction coin bas droite if(col < $dataMap.width - 1 && !$gameMap.colliderMap[row][col + 1][0] && row < $dataMap.height - 1 && !$gameMap.colliderMap[row + 1][col][2]) { this.context.fillRect((col - $gameMap.firstColIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, (row - $gameMap.firstLineIndex) * TILE_SIZE + TILE_SIZE * 3 / 4, TILE_SIZE / 4, TILE_SIZE / 4); } } }; })();