/*:
* @target MZ
* @plugindesc HUD map néon — leader + mini portraits — fade after stop (0.5s) — scalable for many heroes
* @author Bertran
* @help
* Installation :
* - Colle ce fichier en tant que TF_HUD_Map.js dans /js/plugins/
* - Active le plugin dans le Plugin Manager
* - Place les images dans /img/pictures/ (voir la liste ci‑dessous)
*
* Images attendues (placeholders acceptés) :
* - hud_silhouette.png (silhouette générique, fond transparent)
* - hud_frame.png (cadre/plateau du HUD, fond transparent)
* - hud_mini_frame.png (cadre mini portrait)
*
* Le plugin utilise les faces/portraits des acteurs si disponibles.
* Par défaut, couleurs néon (hex) :
* - leader1 (Kou) : #FFD166 (or)
* - leader2 (Sakura): #00E5FF (cyan)
* - leader3 (Ken) : #FF2D95 (fuchsia)
* - leader4 (Asuka) : #FF3B3B (red)
*
* Le HUD apparaît 0.5s après immobilité et disparaît au mouvement.
*
* Paramètres internes modifiables dans OP_CONFIG ci‑dessous.
*/
(() => {
const PLUGIN_NAME = "TF_HUD_Map";
// -------------------------
// CONFIG
// -------------------------
const OP_CONFIG = {
x: 24, // position X (haut gauche)
y: 24, // position Y (haut gauche)
leaderSize: 140, // taille du grand portrait (px)
miniSize: 56, // taille mini portraits
miniSpacing: 8,
idleDelay: 30, // frames (0.5s @60fps)
fadeDuration: 18, // frames for fade in/out
slideDistance: 18, // px slide on appear/disappear
silhouetteImage: "hud_silhouette",
frameImage: "hud_frame",
miniFrameImage: "hud_mini_frame",
colors: [
"#FFD166", // leader 1 - gold
"#00E5FF", // leader 2 - cyan
"#FF2D95", // leader 3 - fuchsia
"#FF3B3B" // leader 4 - red
],
maxMiniToShow: 3, // number of mini portraits under leader
hideInBattle: true,
hideInMenu: true,
hideInMessage: true
};
// -------------------------
// Utilities
// -------------------------
function hexToRgb(hex) {
const h = hex.replace("#", "");
const bigint = parseInt(h, 16);
return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
}
// -------------------------
// Hook into Scene_Map
// -------------------------
const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
Scene_Map.prototype.createAllWindows = function() {
_Scene_Map_createAllWindows.call(this);
if (!this._tfHud) {
this._tfHud = new TF_HUD();
this.addChild(this._tfHud);
}
};
// -------------------------
// HUD Sprite Container
// -------------------------
function TF_HUD() {
this.initialize(...arguments);
}
TF_HUD.prototype = Object.create(Sprite.prototype);
TF_HUD.prototype.constructor = TF_HUD;
TF_HUD.prototype.initialize = function() {
Sprite.prototype.initialize.call(this);
this.x = OP_CONFIG.x - OP_CONFIG.slideDistance;
this.y = OP_CONFIG.y;
this.opacity = 0;
this._visibleState = false; // currently shown
this._idleCounter = 0;
this._fadeTimer = 0;
this._creating = true;
this._sprites = {};
this.createBase();
this._creating = false;
};
TF_HUD.prototype.createBase = function() {
// frame background
const frame = new Sprite(ImageManager.loadPicture(OP_CONFIG.frameImage));
frame.anchor.x = 0;
frame.anchor.y = 0;
frame.x = 0;
frame.y = 0;
frame.opacity = 0;
this.addChild(frame);
this._sprites.frame = frame;
// leader silhouette (base)
const leader = new Sprite(ImageManager.loadPicture(OP_CONFIG.silhouetteImage));
leader.anchor.x = 0;
leader.anchor.y = 0;
leader.x = 8;
leader.y = 8;
leader.opacity = 0;
leader.scale.x = OP_CONFIG.leaderSize / Math.max(leader.bitmap.width || OP_CONFIG.leaderSize, 1);
leader.scale.y = OP_CONFIG.leaderSize / Math.max(leader.bitmap.height || OP_CONFIG.leaderSize, 1);
this.addChild(leader);
this._sprites.leader = leader;
// leader portrait overlay (for actor face if available)
const leaderPortrait = new Sprite();
leaderPortrait.anchor.x = 0;
leaderPortrait.anchor.y = 0;
leaderPortrait.x = leader.x;
leaderPortrait.y = leader.y;
leaderPortrait.opacity = 0;
this.addChild(leaderPortrait);
this._sprites.leaderPortrait = leaderPortrait;
// name / level text
const nameSprite = new Sprite();
nameSprite.x = OP_CONFIG.leaderSize + 16;
nameSprite.y = 16;
this.addChild(nameSprite);
this._sprites.name = nameSprite;
// hp/mp bars container
const bars = new Sprite();
bars.x = OP_CONFIG.leaderSize + 16;
bars.y = 48;
bars.opacity = 0;
this.addChild(bars);
this._sprites.bars = bars;
// mini portraits container
this._sprites.minis = [];
for (let i = 0; i < OP_CONFIG.maxMiniToShow; i++) {
const mFrame = new Sprite(ImageManager.loadPicture(OP_CONFIG.miniFrameImage));
mFrame.anchor.x = 0;
mFrame.anchor.y = 0;
mFrame.x = 8;
mFrame.y = OP_CONFIG.leaderSize + 12 + i * (OP_CONFIG.miniSize + OP_CONFIG.miniSpacing);
mFrame.opacity = 0;
this.addChild(mFrame);
const miniSil = new Sprite(ImageManager.loadPicture(OP_CONFIG.silhouetteImage));
miniSil.anchor.x = 0;
miniSil.anchor.y = 0;
miniSil.x = mFrame.x + 6;
miniSil.y = mFrame.y + 6;
miniSil.opacity = 0;
miniSil.scale.x = OP_CONFIG.miniSize / Math.max(miniSil.bitmap.width || OP_CONFIG.miniSize, 1);
miniSil.scale.y = OP_CONFIG.miniSize / Math.max(miniSil.bitmap.height || OP_CONFIG.miniSize, 1);
this.addChild(miniSil);
const miniPortrait = new Sprite();
miniPortrait.anchor.x = 0;
miniPortrait.anchor.y = 0;
miniPortrait.x = miniSil.x;
miniPortrait.y = miniSil.y;
miniPortrait.opacity = 0;
this.addChild(miniPortrait);
this._sprites.minis.push({
frame: mFrame,
sil: miniSil,
portrait: miniPortrait
});
}
};
// -------------------------
// Update loop
// -------------------------
TF_HUD.prototype.update = function() {
Sprite.prototype.update.call(this);
if (this._creating) return;
this._visibleState = true; // TEST : force le HUD à être visible
// auto-hide conditions
if (this.shouldForceHide()) {
this.forceHide();
return;
}
// movement detection
if (this.playerIsMoving()) {
this._idleCounter = 0;
if (this._visibleState) this.startFadeOut();
} else {
this._idleCounter++;
if (!this._visibleState && this._idleCounter >= OP_CONFIG.idleDelay) {
this.startFadeIn();
}
}
// fade handling
this.updateFade();
// update content if visible (or during fade)
if (this._visibleState || this._fadeTimer > 0) {
this.updateContent();
}
};
TF_HUD.prototype.playerIsMoving = function() {
return $gamePlayer.isMoving();
};
TF_HUD.prototype.shouldForceHide = function() {
if (OP_CONFIG.hideInBattle && $gameParty.inBattle()) return true;
if (OP_CONFIG.hideInMenu && SceneManager._scene instanceof Scene_MenuBase) return true;
if (OP_CONFIG.hideInMessage && $gameMessage.isBusy()) return true;
if ($gamePlayer.isTransferring()) return true;
return false;
};
TF_HUD.prototype.forceHide = function() {
this._idleCounter = 0;
if (this._visibleState || this.opacity > 0) {
this._visibleState = false;
this._fadeTimer = OP_CONFIG.fadeDuration;
this._fadeDirection = -1;
}
};
TF_HUD.prototype.startFadeIn = function() {
this._visibleState = true;
this._fadeTimer = OP_CONFIG.fadeDuration;
this._fadeDirection = 1;
// slide start offset
this.x = OP_CONFIG.x - OP_CONFIG.slideDistance;
};
TF_HUD.prototype.startFadeOut = function() {
this._visibleState = false;
this._fadeTimer = OP_CONFIG.fadeDuration;
this._fadeDirection = -1;
};
TF_HUD.prototype.updateFade = function() {
if (this._fadeTimer > 0) {
const d = this._fadeDirection;
const t = this._fadeTimer;
const total = OP_CONFIG.fadeDuration;
const progress = (total - t + 1) / total;
if (d === 1) {
this.opacity = Math.min(255, Math.floor(255 * progress));
this.x = OP_CONFIG.x - Math.floor(OP_CONFIG.slideDistance * (1 - progress));
} else {
this.opacity = Math.max(0, Math.floor(255 * (1 - progress)));
this.x = OP_CONFIG.x - Math.floor(OP_CONFIG.slideDistance * progress);
}
this._fadeTimer--;
if (this._fadeTimer === 0 && this._fadeDirection === -1) {
// ensure fully hidden
this.opacity = 0;
this.x = OP_CONFIG.x - OP_CONFIG.slideDistance;
} else if (this._fadeTimer === 0 && this._fadeDirection === 1) {
this.opacity = 255;
this.x = OP_CONFIG.x;
}
}
};
// -------------------------
// Content update
// -------------------------
TF_HUD.prototype.updateContent = function() {
const party = $gameParty.members();
if (!party || party.length === 0) return;
// leader is first in party
const leader = party[0];
this.updateLeader(leader);
this.updateMinis(party.slice(1, 1 + OP_CONFIG.maxMiniToShow));
};
TF_HUD.prototype.updateLeader = function(actor) {
if (!actor) return;
const idx = $gameParty.members().indexOf(actor);
const color = OP_CONFIG.colors[idx] || OP_CONFIG.colors[0];
const rgb = hexToRgb(color);
// apply glow via blend color on silhouette
const sil = this._sprites.leader;
sil.bitmap = ImageManager.loadPicture(OP_CONFIG.silhouetteImage);
sil.setBlendColor([rgb[0], rgb[1], rgb[2], 160]); // alpha for glow
sil.opacity = this.opacity;
// try to load actor face or face image as portrait overlay
const portrait = this._sprites.leaderPortrait;
const faceName = actor.faceName();
if (faceName) {
portrait.bitmap = ImageManager.loadFace(faceName);
portrait.scale.x = (OP_CONFIG.leaderSize / 144) * 2; // approximate scale
portrait.scale.y = (OP_CONFIG.leaderSize / 144) * 2;
portrait.opacity = this.opacity;
} else {
portrait.bitmap = null;
portrait.opacity = 0;
}
// name + level
const nameSprite = this._sprites.name;
nameSprite.bitmap = new Bitmap(300, 48);
nameSprite.bitmap.clear();
nameSprite.bitmap.fontSize = 20;
nameSprite.bitmap.textColor = "#FFFFFF";
nameSprite.bitmap.drawText(actor.name(), 0, 0, 300, 24, "left");
nameSprite.bitmap.fontSize = 16;
nameSprite.bitmap.textColor = color;
nameSprite.bitmap.drawText("Lv " + actor.level, 0, 24, 300, 20, "left");
nameSprite.opacity = this.opacity;
// HP / MP bars
const bars = this._sprites.bars;
const w = 220;
const h = 10;
bars.bitmap = new Bitmap(w, h * 2 + 6);
bars.bitmap.clear();
// HP
const hpRate = actor.hpRate();
const mpRate = actor.mpRate();
// background
bars.bitmap.fillRect(0, 0, w, h, "rgba(0,0,0,0.5)");
bars.bitmap.fillRect(0, h + 6, w, h, "rgba(0,0,0,0.5)");
// hp bar
bars.bitmap.fillRect(0, 0, Math.floor(w * hpRate), h, OP_CONFIG.colors[0]); // use leader color? keep gold for visibility
// mp bar
bars.bitmap.fillRect(0, h + 6, Math.floor(w * mpRate), h, "#00AEEF");
bars.opacity = this.opacity;
};
TF_HUD.prototype.updateMinis = function(minis) {
for (let i = 0; i < OP_CONFIG.maxMiniToShow; i++) {
const slot = this._sprites.minis[i];
if (!slot) continue;
const actor = minis[i];
if (actor) {
const partyIndex = $gameParty.members().indexOf(actor);
const color = OP_CONFIG.colors[partyIndex] || OP_CONFIG.colors[(i + 1) % OP_CONFIG.colors.length];
const rgb = hexToRgb(color);
slot.sil.bitmap = ImageManager.loadPicture(OP_CONFIG.silhouetteImage);
slot.sil.setBlendColor([rgb[0], rgb[1], rgb[2], 160]);
slot.sil.opacity = this.opacity;
// portrait if face exists
const faceName = actor.faceName();
if (faceName) {
slot.portrait.bitmap = ImageManager.loadFace(faceName);
slot.portrait.scale.x = (OP_CONFIG.miniSize / 144) * 2;
slot.portrait.scale.y = (OP_CONFIG.miniSize / 144) * 2;
slot.portrait.opacity = this.opacity;
} else {
slot.portrait.bitmap = null;
slot.portrait.opacity = this.opacity;
}
slot.frame.opacity = this.opacity;
} else {
slot.sil.opacity = 0;
slot.portrait.opacity = 0;
slot.frame.opacity = 0;
}
}
};
// -------------------------
// React to party changes (leader swap)
// -------------------------
const _Game_Party_swapOrder = Game_Party.prototype.swapOrder;
Game_Party.prototype.swapOrder = function(index1, index2) {
_Game_Party_swapOrder.call(this, index1, index2);
// force HUD refresh
const scene = SceneManager._scene;
if (scene && scene._tfHud) {
scene._tfHud.updateContent();
}
};
// Also refresh when party members change (add/remove)
const _Game_Party_addActor = Game_Party.prototype.addActor;
Game_Party.prototype.addActor = function(actorId) {
_Game_Party_addActor.call(this, actorId);
const scene = SceneManager._scene;
if (scene && scene._tfHud) scene._tfHud.updateContent();
};
const _Game_Party_removeActor = Game_Party.prototype.removeActor;
Game_Party.prototype.removeActor = function(actorId) {
_Game_Party_removeActor.call(this, actorId);
const scene = SceneManager._scene;
if (scene && scene._tfHud) scene._tfHud.updateContent();
};
// -------------------------
// Clean up on scene end
// -------------------------
const _Scene_Map_terminate = Scene_Map.prototype.terminate;
Scene_Map.prototype.terminate = function() {
_Scene_Map_terminate.call(this);
if (this._tfHud) {
this.removeChild(this._tfHud);
this._tfHud = null;
}
};
})();
To embed this project on your website, copy the following code and paste it into your website's HTML: