/*:
* @target MZ
* @plugindesc Menu principal ultra stylisé - Inspiré Persona 5 + Street Fighter 6
* @author Akira for Akira1kurusu
* @version 1.7
*
* @param menuColor1
* @text Couleur principale
* @type string
* @default #E8003A
*
* @param menuColor2
* @text Couleur secondaire
* @type string
* @default #1A1A1A
*
* @param accentColor
* @text Couleur accent
* @type string
* @default #FFFFFF
*
* @param paintColor
* @text Couleur effet peinture
* @type string
* @default #FFD700
*
* @param transitionSpeed
* @text Vitesse de transition (frames)
* @type number
* @min 5
* @max 60
* @default 30
*
* @help
* UNIQUE MENU v1.7 — Persona 5 x Street Fighter 6
* Transitions stylisées, formation corrigée, gold window améliorée.
*/
(() => {
'use strict';
const pluginName = 'UniqueMenu_MZ';
const params = PluginManager.parameters(pluginName);
const COLOR1 = params.menuColor1 || '#E8003A';
const COLOR2 = params.menuColor2 || '#1A1A1A';
const ACCENT = params.accentColor || '#FFFFFF';
const PAINT = params.paintColor || '#FFD700';
const TRANS_SPEED = parseInt(params.transitionSpeed) || 30;
const GOLD_H = 96;
const BAR_WIDTH = 14;
// ============================================================
// UTILITAIRES CANVAS
// ============================================================
function getCtx(bmp) {
return bmp && bmp._canvas ? bmp._canvas.getContext('2d') : null;
}
function drawSkewedRect(ctx, x, y, w, h, skew, color, alpha) {
if (alpha === undefined) alpha = 1;
ctx.save();
ctx.globalAlpha = alpha; ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(x + skew, y);
ctx.lineTo(x + w + skew, y);
ctx.lineTo(x + w - skew, y + h);
ctx.lineTo(x - skew, y + h);
ctx.closePath(); ctx.fill(); ctx.restore();
}
function drawPaintSplash(ctx, cx, cy, size, color, alpha) {
if (alpha === undefined) alpha = 0.7;
ctx.save();
ctx.globalAlpha = alpha; ctx.fillStyle = color;
const pts = 8 + Math.floor(Math.random() * 5);
ctx.beginPath();
for (let i = 0; i < pts * 2; i++) {
const ang = (i / (pts * 2)) * Math.PI * 2;
const r = (i % 2 === 0)
? size * (0.7 + Math.random() * 0.5)
: size * (0.3 + Math.random() * 0.3);
if (i === 0) ctx.moveTo(cx + Math.cos(ang) * r, cy + Math.sin(ang) * r);
else ctx.lineTo(cx + Math.cos(ang) * r, cy + Math.sin(ang) * r);
}
ctx.closePath(); ctx.fill(); ctx.restore();
}
function drawSpeedLines(ctx, cx, cy, count, length, color, alpha) {
if (alpha === undefined) alpha = 0.12;
ctx.save();
ctx.globalAlpha = alpha; ctx.strokeStyle = color; ctx.lineWidth = 1;
for (let i = 0; i < count; i++) {
const ang = (i / count) * Math.PI * 2;
const s = 20 + Math.random() * 30;
const e = s + length * (0.5 + Math.random() * 0.5);
ctx.beginPath();
ctx.moveTo(cx + Math.cos(ang) * s, cy + Math.sin(ang) * s);
ctx.lineTo(cx + Math.cos(ang) * e, cy + Math.sin(ang) * e);
ctx.stroke();
}
ctx.restore();
}
function drawBg(ctx, w, h, opts) {
opts = opts || {};
const bgAlpha = opts.bgAlpha !== undefined ? opts.bgAlpha : 0.93;
const barColor = opts.barColor || COLOR1;
const barSide = opts.barSide || 'left';
const barSize = opts.barSize || 6;
const splashes = Array.isArray(opts.splashes) ? opts.splashes : [];
const lines = opts.speedLines !== false;
ctx.save(); ctx.globalAlpha = bgAlpha; ctx.fillStyle = COLOR2;
ctx.fillRect(0, 0, w, h); ctx.restore();
ctx.save(); ctx.globalAlpha = 1; ctx.fillStyle = barColor;
if (barSide === 'left') ctx.fillRect(0, 0, barSize, h);
else ctx.fillRect(0, 0, w, barSize);
ctx.restore();
for (const s of splashes) drawPaintSplash(ctx, s.x, s.y, s.size, s.color, s.alpha);
if (lines) drawSpeedLines(ctx, w * 0.85, h * 0.5, 28, h * 0.6, PAINT, 0.05);
ctx.save(); ctx.strokeStyle = COLOR1; ctx.globalAlpha = 0.18;
ctx.lineWidth = 1.5; ctx.beginPath();
ctx.moveTo(w * 0.55, 0); ctx.lineTo(w, h * 0.4);
ctx.stroke(); ctx.restore();
}
function p5Text(ctx, text, x, y, opts) {
opts = opts || {};
const sz = opts.size || 16;
const color = opts.color || ACCENT;
ctx.save();
ctx.font = `bold ${sz}px "Arial Black", Arial, sans-serif`;
ctx.textBaseline = opts.baseline || 'middle';
ctx.textAlign = opts.align || 'left';
if (opts.shadow !== false) {
ctx.fillStyle = '#000'; ctx.globalAlpha = 0.65;
ctx.fillText(text, x + 2, y + 2);
}
ctx.fillStyle = color;
ctx.globalAlpha = opts.alpha !== undefined ? opts.alpha : 1;
ctx.fillText(text, x, y);
ctx.restore();
}
function drawStatBar(ctx, x, y, w, h, ratio, fillColor) {
ctx.save();
ctx.fillStyle = '#2a2a2a'; ctx.globalAlpha = 0.9;
ctx.fillRect(x, y, w, h);
ctx.fillStyle = fillColor; ctx.globalAlpha = 1;
ctx.fillRect(x, y, Math.max(0, w * ratio), h);
ctx.fillStyle = '#fff'; ctx.globalAlpha = 0.12;
ctx.fillRect(x, y, Math.max(0, w * ratio), Math.ceil(h / 2));
ctx.restore();
}
// ============================================================
// MIXIN STYLE GÉNÉRIQUE — sans toucher aux prototypes pour opacity
// ============================================================
function applyUniqueStyle(WinClass) {
const _init = WinClass.prototype.initialize;
WinClass.prototype.initialize = function (rect) {
_init.call(this, rect);
this.opacity = 0; this.frameVisible = false;
};
const _ref = WinClass.prototype.refresh;
WinClass.prototype.refresh = function () {
if (this.contents) {
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'left', barSize: 5, speedLines: false });
}
_ref.call(this);
};
}
[Window_ItemCategory, Window_SkillType, Window_EquipItem,
Window_SavefileList, Window_Options, Window_GameEnd
].forEach(applyUniqueStyle);
// Window_Help
const _HI = Window_Help.prototype.initialize;
Window_Help.prototype.initialize = function (rect) {
_HI.call(this, rect); this.opacity = 0; this.frameVisible = false;
};
const _HR = Window_Help.prototype.refresh;
Window_Help.prototype.refresh = function () {
if (this.contents) {
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'top', barSize: 4, speedLines: false });
}
_HR.call(this);
};
// ============================================================
// WINDOW_ITEMLIST
// ============================================================
const _ILI = Window_ItemList.prototype.initialize;
Window_ItemList.prototype.initialize = function (rect) {
_ILI.call(this, rect); this.opacity = 0; this.frameVisible = false;
};
const _ILR = Window_ItemList.prototype.refresh;
Window_ItemList.prototype.refresh = function () {
if (this.contents) {
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'left', barSize: 5, speedLines: false });
}
_ILR.call(this);
};
Window_ItemList.prototype.drawItem = function (index) {
const item = this.itemAt(index);
if (!item) return;
const rect = this.itemLineRect(index);
const ctx = getCtx(this.contents);
if (!ctx) { Window_Selectable.prototype.drawItem.call(this, index); return; }
const enabled = this.isEnabled(item);
const pad = 4, skew = 8;
drawSkewedRect(ctx, rect.x + pad, rect.y + pad,
rect.width - pad * 2, rect.height - pad * 2, skew, ACCENT, enabled ? 0.07 : 0.03);
this.drawIcon(item.iconIndex, rect.x + 8,
rect.y + (rect.height - ImageManager.iconHeight) / 2);
p5Text(ctx, item.name.toUpperCase(), rect.x + 40, rect.y + rect.height / 2,
{ size: 15, color: enabled ? ACCENT : '#666', shadow: true });
const count = $gameParty.numItems(item);
const countStr = `×${count}`;
ctx.save();
ctx.font = 'bold 13px "Arial Black", Arial, sans-serif';
ctx.textBaseline = 'middle'; ctx.textAlign = 'right';
const cw = ctx.measureText(countStr).width + 12;
drawSkewedRect(ctx, rect.x + rect.width - cw - pad - 4, rect.y + pad + 2,
cw, rect.height - pad * 2 - 4, 4, PAINT, enabled ? 0.18 : 0.07);
ctx.fillStyle = PAINT; ctx.globalAlpha = enabled ? 1 : 0.4;
ctx.fillText(countStr, rect.x + rect.width - pad - 6, rect.y + rect.height / 2);
ctx.restore();
};
// ============================================================
// WINDOW_SKILLLIST
// ============================================================
const _SLI = Window_SkillList.prototype.initialize;
Window_SkillList.prototype.initialize = function (rect) {
_SLI.call(this, rect); this.opacity = 0; this.frameVisible = false;
};
const _SLR = Window_SkillList.prototype.refresh;
Window_SkillList.prototype.refresh = function () {
if (this.contents) {
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'left', barSize: 5, speedLines: false });
}
_SLR.call(this);
};
Window_SkillList.prototype.drawItem = function (index) {
const skill = this.itemAt(index);
if (!skill) return;
const rect = this.itemLineRect(index);
const ctx = getCtx(this.contents);
const actor = this._actor;
if (!ctx || !actor) { Window_Selectable.prototype.drawItem.call(this, index); return; }
const enabled = this.isEnabled(skill);
const pad = 4, skew = 8;
drawSkewedRect(ctx, rect.x + pad, rect.y + pad,
rect.width - pad * 2, rect.height - pad * 2, skew, ACCENT, enabled ? 0.07 : 0.03);
this.drawIcon(skill.iconIndex, rect.x + 8,
rect.y + (rect.height - ImageManager.iconHeight) / 2);
p5Text(ctx, skill.name.toUpperCase(), rect.x + 40, rect.y + rect.height / 2,
{ size: 15, color: enabled ? ACCENT : '#666', shadow: true });
const cost = actor.skillMpCost(skill);
const tp = actor.skillTpCost(skill);
if (cost > 0 || tp > 0) {
const label = cost > 0 ? `${cost} MP` : `${tp} TP`;
const lcolor = cost > 0 ? '#5599FF' : '#33DDAA';
ctx.save();
ctx.font = 'bold 12px "Arial Black", Arial, sans-serif';
ctx.textBaseline = 'middle'; ctx.textAlign = 'right';
const lw = ctx.measureText(label).width + 10;
drawSkewedRect(ctx, rect.x + rect.width - lw - pad - 4,
rect.y + pad + 2, lw, rect.height - pad * 2 - 4, 4, lcolor, 0.2);
ctx.fillStyle = lcolor; ctx.globalAlpha = enabled ? 1 : 0.4;
ctx.fillText(label, rect.x + rect.width - pad - 6, rect.y + rect.height / 2);
ctx.restore();
}
};
// ============================================================
// WINDOW_EQUIPSLOT
// ============================================================
const _ESI = Window_EquipSlot.prototype.initialize;
Window_EquipSlot.prototype.initialize = function (rect) {
_ESI.call(this, rect); this.opacity = 0; this.frameVisible = false;
};
const _ESR = Window_EquipSlot.prototype.refresh;
Window_EquipSlot.prototype.refresh = function () {
if (this.contents) {
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'left', barSize: 5, speedLines: false });
}
_ESR.call(this);
};
Window_EquipSlot.prototype.drawItem = function (index) {
if (!this._actor) return;
const rect = this.itemLineRect(index);
const ctx = getCtx(this.contents);
if (!ctx) { Window_StatusBase.prototype.drawItem.call(this, index); return; }
const slotName = this.slotName(index);
const item = this._actor.equips()[index];
const isSelected = (index === this.index());
const pad = 4, skew = 6;
drawSkewedRect(ctx, rect.x + pad, rect.y + pad,
rect.width - pad * 2, rect.height - pad * 2, skew,
isSelected ? COLOR1 : ACCENT, isSelected ? 0.35 : 0.06);
p5Text(ctx, slotName.toUpperCase(), rect.x + 10, rect.y + rect.height / 2,
{ size: 11, color: COLOR1, shadow: false });
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 0.4;
ctx.fillRect(rect.x + 10, rect.y + rect.height - 3, rect.width - 20, 1);
ctx.restore();
if (item) {
this.drawIcon(item.iconIndex, rect.x + rect.width / 2 - 60,
rect.y + (rect.height - ImageManager.iconHeight) / 2);
p5Text(ctx, item.name.toUpperCase(),
rect.x + rect.width / 2 - 36, rect.y + rect.height / 2,
{ size: 14, color: isSelected ? PAINT : ACCENT, shadow: true });
} else {
p5Text(ctx, '—', rect.x + rect.width / 2, rect.y + rect.height / 2,
{ size: 14, color: '#555', shadow: false, align: 'center' });
}
};
// ============================================================
// WINDOW_STATUS
// ============================================================
const _WSI = Window_Status.prototype.initialize;
Window_Status.prototype.initialize = function (rect) {
_WSI.call(this, rect); this.opacity = 0; this.frameVisible = false;
};
Window_Status.prototype.refresh = function () {
if (!this.contents || !this._actor) return;
this.contents.clear();
const ctx = getCtx(this.contents);
const actor = this._actor;
const w = this.contentsWidth(), h = this.contentsHeight();
if (!ctx) { Window_StatusBase.prototype.refresh.call(this); return; }
drawBg(ctx, w, h, { barSide: 'left', barSize: 6, speedLines: true });
drawPaintSplash(ctx, w - 50, 30, 40, PAINT, 0.06);
drawPaintSplash(ctx, w - 30, h - 20, 25, COLOR1, 0.05);
// ── PORTRAIT ──────────────────────────────────────────
const faceW = 128, faceH = 128;
this.drawFace(actor.faceName(), actor.faceIndex(), 14, 14, faceW, faceH);
ctx.save();
ctx.strokeStyle = COLOR1; ctx.globalAlpha = 0.9; ctx.lineWidth = 3;
ctx.strokeRect(12, 12, faceW + 4, faceH + 4);
ctx.strokeStyle = PAINT; ctx.globalAlpha = 0.4; ctx.lineWidth = 1;
ctx.strokeRect(8, 8, faceW + 12, faceH + 12);
ctx.restore();
// ── NOM + CLASSE + NIVEAU ─────────────────────────────
const infoX = faceW + 28;
p5Text(ctx, actor.name().toUpperCase(), infoX, 28, { size: 28, color: ACCENT });
p5Text(ctx, actor.currentClass().name.toUpperCase(), infoX + 2, 60,
{ size: 14, color: COLOR1, shadow: false });
// Badge niveau
drawSkewedRect(ctx, infoX, 72, 100, 28, 7, COLOR1, 0.95);
p5Text(ctx, `LV. ${actor.level}`, infoX + 8, 86, { size: 16, color: '#000', shadow: false });
// ── HP / MP BARRES LARGES ─────────────────────────────
const hpRatio = actor.mhp > 0 ? actor.hp / actor.mhp : 0;
const mpRatio = actor.mmp > 0 ? actor.mp / actor.mmp : 0;
const hpColor = hpRatio > 0.5 ? '#00DD55' : hpRatio > 0.25 ? '#FFAA00' : COLOR1;
const mpColor = '#5599FF';
const barW = w - infoX - 16;
// HP
ctx.save();
ctx.font = 'bold 12px "Arial Black", Arial'; ctx.textBaseline = 'middle';
ctx.fillStyle = hpColor; ctx.globalAlpha = 1;
ctx.fillText('HP', infoX, 112);
ctx.font = '11px Arial'; ctx.fillStyle = ACCENT; ctx.globalAlpha = 0.95;
ctx.fillText(`${actor.hp} / ${actor.mhp}`, infoX + 28, 112);
ctx.restore();
drawStatBar(ctx, infoX, 122, barW, 9, hpRatio, hpColor);
// MP
ctx.save();
ctx.font = 'bold 12px "Arial Black", Arial'; ctx.textBaseline = 'middle';
ctx.fillStyle = mpColor; ctx.globalAlpha = 1;
ctx.fillText('MP', infoX, 142);
ctx.font = '11px Arial'; ctx.fillStyle = ACCENT; ctx.globalAlpha = 0.95;
ctx.fillText(`${actor.mp} / ${actor.mmp}`, infoX + 28, 142);
ctx.restore();
drawStatBar(ctx, infoX, 152, barW, 9, mpRatio, mpColor);
// EXP
const expRate = actor.isMaxLevel() ? 1
: (actor.currentExp() - actor.currentLevelExp()) /
(actor.nextLevelExp() - actor.currentLevelExp());
ctx.save();
ctx.font = 'bold 10px "Arial Black", Arial'; ctx.textBaseline = 'middle';
ctx.fillStyle = PAINT; ctx.globalAlpha = 1;
ctx.fillText('EXP', infoX, 170);
ctx.font = '10px Arial'; ctx.fillStyle = ACCENT; ctx.globalAlpha = 0.7;
const expPct = Math.floor(expRate * 100);
ctx.fillText(`${expPct}%`, infoX + 32, 170);
ctx.restore();
drawStatBar(ctx, infoX, 178, barW, 6, expRate, PAINT);
// ── SÉPARATEUR ────────────────────────────────────────
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 0.55;
ctx.fillRect(10, faceH + 22, w - 20, 2); ctx.restore();
// ── GRILLE DE STATS ──────────────────────────────────
// 4 colonnes x 2 lignes pour plus de lisibilité
const stats = [
{ label: 'ATK', val: actor.atk, color: '#FF6666' },
{ label: 'DEF', val: actor.def, color: '#66AAFF' },
{ label: 'M.ATK', val: actor.mat, color: '#CC66FF' },
{ label: 'M.DEF', val: actor.mdf, color: '#66CCFF' },
{ label: 'VITESSE', val: actor.agi, color: '#66FF99' },
{ label: 'CHANCE', val: actor.luk, color: PAINT },
{ label: 'HP MAX', val: actor.mhp, color: hpColor },
{ label: 'MP MAX', val: actor.mmp, color: mpColor },
];
const gridY = faceH + 30;
const cols = 4;
const colW = Math.floor((w - 20) / cols);
const rowH = Math.floor((h - gridY - 8) / 2);
stats.forEach((st, i) => {
const col = i % cols;
const row = Math.floor(i / cols);
const sx = 10 + col * colW;
const sy = gridY + row * rowH;
// Fond de la cellule
drawSkewedRect(ctx, sx + 2, sy + 3, colW - 4, rowH - 6, 4, ACCENT, 0.06);
// Trait coloré à gauche de la cellule
ctx.save();
ctx.fillStyle = st.color; ctx.globalAlpha = 0.85;
ctx.fillRect(sx + 2, sy + 5, 3, rowH - 10);
ctx.restore();
// Label
p5Text(ctx, st.label, sx + 10, sy + rowH / 2 - 8,
{ size: 9, color: st.color, shadow: false });
// Valeur (grande)
p5Text(ctx, String(st.val), sx + 10, sy + rowH / 2 + 8,
{ size: 18, color: ACCENT, shadow: true });
});
};
// ============================================================
// WINDOW_MENU_COMMAND
// ============================================================
class Window_UniqueMenuCommand extends Window_MenuCommand {
constructor(rect) {
super(rect);
this._splashes = [];
this._hoverAlphas = new Array(10).fill(0);
this._animFrame = 0;
this._lastIndex = -1;
this._selectedFlash = 0;
this.opacity = 0;
this.frameVisible = false;
this._generateItemSplashes();
}
_generateItemSplashes() {
this._splashes = [];
const w = this.width || 300, h = this.height || 400;
for (let i = 0; i < 8; i++) {
this._splashes.push({
x: w * 0.75 + Math.random() * w * 0.5,
y: 20 + Math.random() * h,
size: 12 + Math.random() * 30,
color: Math.random() > 0.6 ? COLOR1 : PAINT,
alpha: 0.05 + Math.random() * 0.09
});
}
}
itemHeight() { return 62; }
itemPadding() { return 16; }
maxCols() { return 1; }
update() {
super.update();
this._animFrame++;
if (!Array.isArray(this._hoverAlphas)) this._hoverAlphas = new Array(10).fill(0);
if (!Array.isArray(this._splashes)) this._splashes = [];
const idx = this.index();
for (let i = 0; i < this.maxItems(); i++) {
if (i === idx) this._hoverAlphas[i] = Math.min(1, (this._hoverAlphas[i] || 0) + 0.08);
else this._hoverAlphas[i] = Math.max(0, (this._hoverAlphas[i] || 0) - 0.05);
}
if (idx !== this._lastIndex) { this._lastIndex = idx; this._selectedFlash = 15; this.refresh(); }
if (this._selectedFlash > 0) { this._selectedFlash--; this.refresh(); }
if (this._animFrame % 4 === 0) this.refresh();
}
_refreshBack() {}
refresh() {
if (!this.contents) return;
if (!Array.isArray(this._splashes)) this._splashes = [];
if (!Array.isArray(this._hoverAlphas)) this._hoverAlphas = new Array(10).fill(0);
this.contents.clear();
const ctx = getCtx(this.contents);
if (ctx) drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'left', barSize: 8, splashes: this._splashes });
super.refresh();
}
drawItem(index) {
const rect = this.itemLineRect(index);
const ctx = getCtx(this.contents);
if (!ctx) { super.drawItem(index); return; }
const name = this.commandName(index);
const enabled = this.isCommandEnabled(index);
const isSelected = (index === this.index());
const hoverAlpha = (this._hoverAlphas && this._hoverAlphas[index]) || 0;
const skew = 12, pad = 4;
drawSkewedRect(ctx, rect.x + BAR_WIDTH + pad, rect.y + pad,
rect.width - BAR_WIDTH - pad * 2, rect.height - pad * 2, skew, '#000', 0.25);
drawSkewedRect(ctx, rect.x + BAR_WIDTH + pad + 2, rect.y + pad,
rect.width - BAR_WIDTH - pad * 2 - 2, rect.height - pad * 2, skew,
ACCENT, enabled ? (0.15 + hoverAlpha * 0.25) : 0.07);
if (isSelected) {
const fb = this._selectedFlash > 0 ? (this._selectedFlash / 15) * 0.3 : 0;
drawSkewedRect(ctx, rect.x + BAR_WIDTH + pad + 2, rect.y + pad,
rect.width - BAR_WIDTH - pad * 2 - 2, rect.height - pad * 2, skew, COLOR1, 0.55 + fb);
if (this._selectedFlash > 8)
drawPaintSplash(ctx, rect.x + rect.width - 30, rect.y + rect.height / 2,
25 + (this._selectedFlash / 15) * 20, PAINT, 0.6 + (this._selectedFlash / 15) * 0.3);
ctx.save();
ctx.fillStyle = PAINT;
ctx.globalAlpha = 0.9 + Math.sin(this._animFrame * 0.15) * 0.1;
ctx.beginPath();
ctx.moveTo(rect.x + BAR_WIDTH + pad - 2, rect.y + rect.height / 2 - 8);
ctx.lineTo(rect.x + BAR_WIDTH + pad + 10, rect.y + rect.height / 2);
ctx.lineTo(rect.x + BAR_WIDTH + pad - 2, rect.y + rect.height / 2 + 8);
ctx.closePath(); ctx.fill(); ctx.restore();
}
const textX = rect.x + BAR_WIDTH + pad + skew + 12;
const textY = rect.y + rect.height / 2;
const fontSize = isSelected ? 22 : 19;
ctx.save();
ctx.font = `bold ${fontSize}px "Arial Black", Arial, sans-serif`;
ctx.textBaseline = 'middle';
ctx.fillStyle = '#000'; ctx.globalAlpha = 0.6;
ctx.fillText(name.toUpperCase(), textX + 2, textY + 2);
ctx.fillStyle = !enabled ? '#888' : ACCENT;
ctx.globalAlpha = !enabled ? 0.5 : isSelected ? 1 : 0.85;
ctx.fillText(name.toUpperCase(), textX, textY);
if (isSelected) {
const tw = ctx.measureText(name.toUpperCase()).width;
ctx.fillStyle = PAINT; ctx.globalAlpha = 0.9;
ctx.fillRect(textX, textY + fontSize / 2 + 2, tw * 0.6, 2);
}
ctx.restore();
}
_updateCursor() { this.setCursorRect(0, 0, 0, 0); }
}
// ============================================================
// WINDOW_MENU_STATUS
// ============================================================
class Window_UniqueMenuStatus extends Window_MenuStatus {
constructor(rect) {
super(rect);
this.opacity = 0; this.frameVisible = false;
}
refresh() {
if (!this.contents) return;
this.contents.clear();
const ctx = getCtx(this.contents);
if (ctx) {
drawBg(ctx, this.contentsWidth(), this.contentsHeight(),
{ barSide: 'top', barSize: 5, speedLines: false });
p5Text(ctx, 'PARTY', 12, 18, { size: 13, color: COLOR1 });
ctx.save(); ctx.fillStyle = PAINT; ctx.globalAlpha = 0.6;
ctx.fillRect(12, 30, 50, 2); ctx.restore();
}
super.refresh();
}
drawItem(index) {
const actor = $gameParty.members()[index];
if (!actor) return;
const rect = this.itemRect(index);
const ctx = getCtx(this.contents);
if (!ctx) { super.drawItem(index); return; }
const pad = 5;
const faceSize = 58;
// Fond oblique item
drawSkewedRect(ctx, rect.x + pad, rect.y + pad,
rect.width - pad * 2, rect.height - pad * 2, 6, ACCENT, 0.07);
// Portrait
this.drawFace(actor.faceName(), actor.faceIndex(),
rect.x + rect.width - faceSize - pad - 2,
rect.y + pad,
faceSize, rect.height - pad * 2);
// Nom + niveau
const infoX = rect.x + pad + 10;
p5Text(ctx, actor.name().toUpperCase(), infoX, rect.y + pad + 14, { size: 15 });
p5Text(ctx, `LV. ${actor.level}`, infoX, rect.y + pad + 32,
{ size: 11, color: PAINT, shadow: false });
// Zone barres — occupe le bas de la carte
const barW = rect.width - faceSize - pad * 2 - 20;
const barX = infoX;
const midY = rect.y + rect.height / 2 + 6;
const hpRatio = actor.mhp > 0 ? actor.hp / actor.mhp : 0;
const mpRatio = actor.mmp > 0 ? actor.mp / actor.mmp : 0;
const hpColor = hpRatio > 0.5 ? '#00DD55' : hpRatio > 0.25 ? '#FFAA00' : COLOR1;
const mpColor = '#5599FF';
// --- HP ---
// Label HP
ctx.save();
ctx.font = 'bold 10px "Arial Black", Arial'; ctx.textBaseline = 'middle';
ctx.fillStyle = hpColor; ctx.globalAlpha = 1;
ctx.fillText('HP', barX, midY);
// Valeur HP
ctx.font = '10px Arial';
ctx.fillStyle = ACCENT; ctx.globalAlpha = 0.9;
ctx.fillText(`${actor.hp}/${actor.mhp}`, barX + 22, midY);
ctx.restore();
// Barre HP
drawStatBar(ctx, barX, midY + 8, barW, 6, hpRatio, hpColor);
// --- MP ---
const mpRowY = midY + 22;
ctx.save();
ctx.font = 'bold 10px "Arial Black", Arial'; ctx.textBaseline = 'middle';
ctx.fillStyle = mpColor; ctx.globalAlpha = 1;
ctx.fillText('MP', barX, mpRowY);
ctx.font = '10px Arial';
ctx.fillStyle = ACCENT; ctx.globalAlpha = 0.9;
ctx.fillText(`${actor.mp}/${actor.mmp}`, barX + 22, mpRowY);
ctx.restore();
// Barre MP
drawStatBar(ctx, barX, mpRowY + 8, barW, 6, mpRatio, mpColor);
}
}
// ============================================================
// GOLD WINDOW
// ============================================================
Window_Gold.prototype.refresh = function () {
// PATCH — GOLD BOOST VISIBILITY
Window_Gold.prototype.refresh = function () {
if (!this.contents) return;
this.contents.clear();
const ctx = this.contents.context;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond plus contrasté
drawBg(ctx, w, h, {
barSide: 'top',
barSize: 8,
barColor: PAINT,
bgAlpha: 0.96,
speedLines: false
});
// Badge GOLD plus large
ctx.save();
ctx.fillStyle = COLOR1;
ctx.globalAlpha = 1;
ctx.fillRect(6, 4, 70, 22);
ctx.font = 'bold 13px "Arial Black", Arial';
ctx.fillStyle = ACCENT;
ctx.textBaseline = 'middle';
ctx.fillText('GOLD', 12, 15);
ctx.restore();
// Séparateur
ctx.save();
ctx.fillStyle = PAINT;
ctx.globalAlpha = 0.6;
ctx.fillRect(6, 30, w - 12, 2);
ctx.restore();
// Icône pièce — plus brillante
const cy = 30 + Math.floor((h - 30) / 2);
const iconR = 20;
const iconX = 16 + iconR;
ctx.save();
const grad = ctx.createRadialGradient(iconX - 4, cy - 4, 1, iconX, cy, iconR);
grad.addColorStop(0, '#FFFDE7');
grad.addColorStop(0.4, '#FFD700');
grad.addColorStop(1, '#8B6B00');
ctx.beginPath();
ctx.arc(iconX, cy, iconR, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
// Contour lumineux
ctx.strokeStyle = '#FFF8B0';
ctx.lineWidth = 2;
ctx.globalAlpha = 0.9;
ctx.stroke();
// Lettre G
ctx.fillStyle = '#1A1A1A';
ctx.font = 'bold 15px "Arial Black", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('G', iconX, cy + 1);
ctx.restore();
// Valeur + halo
const value = $gameParty.gold().toLocaleString();
const unit = TextManager.currencyUnit;
const startX = iconX + iconR + 10;
ctx.save();
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
// Halo
ctx.font = 'bold 32px "Arial Black", Arial';
ctx.fillStyle = PAINT;
ctx.globalAlpha = 0.25;
ctx.fillText(value, startX + 3, cy + 3);
// Texte principal
ctx.globalAlpha = 1;
ctx.fillStyle = PAINT;
ctx.fillText(value, startX, cy);
// Badge unité
const vw = ctx.measureText(value).width;
const ux = startX + vw + 14;
const uw = ctx.measureText(unit).width + 18;
const uh = 30;
ctx.fillStyle = COLOR1;
ctx.globalAlpha = 0.95;
ctx.beginPath();
ctx.moveTo(ux + 6, cy - uh / 2);
ctx.lineTo(ux + uw + 6, cy - uh / 2);
ctx.lineTo(ux + uw - 6, cy + uh / 2);
ctx.lineTo(ux - 6, cy + uh / 2);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = PAINT;
ctx.lineWidth = 2;
ctx.globalAlpha = 0.7;
ctx.stroke();
ctx.fillStyle = ACCENT;
ctx.globalAlpha = 1;
ctx.fillText(unit, ux + (uw - ctx.measureText(unit).width) / 2, cy + 1);
ctx.restore();
};
if (!this.contents) return;
this.contents.clear();
const ctx = getCtx(this.contents);
if (!ctx) { Window_Base.prototype.refresh.call(this); return; }
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond sombre + barre dorée en haut
drawBg(ctx, w, h, {
barSide: 'top', barSize: 6, barColor: PAINT,
speedLines: false, splashes: []
});
// Badge "GOLD" en haut à gauche
ctx.save();
ctx.fillStyle = COLOR1; ctx.globalAlpha = 1;
ctx.fillRect(6, 4, 46, 18);
ctx.font = 'bold 11px "Arial Black", Arial, sans-serif';
ctx.fillStyle = ACCENT; ctx.globalAlpha = 1;
ctx.textBaseline = 'middle'; ctx.textAlign = 'left';
ctx.fillText('GOLD', 9, 13);
ctx.restore();
// Séparateur
ctx.save(); ctx.fillStyle = PAINT; ctx.globalAlpha = 0.5;
ctx.fillRect(6, 26, w - 12, 1); ctx.restore();
// Centre vertical de la zone sous le séparateur
const cy = 26 + Math.floor((h - 26) / 2);
// Icône pièce
const iconR = Math.min(18, Math.floor((h - 30) / 2) - 2);
const iconX = 8 + iconR;
const iconY = cy;
ctx.save();
const grad = ctx.createRadialGradient(iconX - 3, iconY - 3, 1, iconX, iconY, iconR);
grad.addColorStop(0, '#FFF59D');
grad.addColorStop(0.4, '#FFD700');
grad.addColorStop(1, '#7B5E00');
ctx.beginPath(); ctx.arc(iconX, iconY, iconR, 0, Math.PI * 2);
ctx.fillStyle = grad; ctx.fill();
ctx.strokeStyle = '#FFE566'; ctx.lineWidth = 1.5; ctx.globalAlpha = 0.9; ctx.stroke();
ctx.fillStyle = '#1A1A1A'; ctx.globalAlpha = 1;
ctx.font = 'bold 13px "Arial Black", Arial';
ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
ctx.fillText('G', iconX, iconY + 1);
ctx.restore();
// Valeur + unité
const value = $gameParty.gold();
const unit = TextManager.currencyUnit;
const valueStr = value.toLocaleString();
const startX = iconX + iconR + 6;
ctx.save();
ctx.textBaseline = 'middle'; ctx.textAlign = 'left';
// Ombre valeur
ctx.font = 'bold 28px "Arial Black", Arial, sans-serif';
ctx.fillStyle = '#000000'; ctx.globalAlpha = 0.55;
ctx.fillText(valueStr, startX + 2, cy + 2);
// Valeur dorée
ctx.fillStyle = PAINT; ctx.globalAlpha = 1;
ctx.fillText(valueStr, startX, cy);
// Unité "Mo" — badge rouge oblique bien lisible
const vw = ctx.measureText(valueStr).width;
const ux = startX + vw + 10;
ctx.font = 'bold 20px "Arial Black", Arial, sans-serif';
const uw = ctx.measureText(unit).width + 14;
const uh = 28;
// Fond oblique rouge
ctx.fillStyle = COLOR1; ctx.globalAlpha = 0.95;
ctx.beginPath();
ctx.moveTo(ux + 5, cy - uh / 2);
ctx.lineTo(ux + uw + 5, cy - uh / 2);
ctx.lineTo(ux + uw - 5, cy + uh / 2);
ctx.lineTo(ux - 5, cy + uh / 2);
ctx.closePath(); ctx.fill();
// Contour PAINT
ctx.strokeStyle = PAINT; ctx.lineWidth = 1.5; ctx.globalAlpha = 0.7;
ctx.beginPath();
ctx.moveTo(ux + 5, cy - uh / 2);
ctx.lineTo(ux + uw + 5, cy - uh / 2);
ctx.lineTo(ux + uw - 5, cy + uh / 2);
ctx.lineTo(ux - 5, cy + uh / 2);
ctx.closePath(); ctx.stroke();
// Texte unité blanc
ctx.fillStyle = ACCENT; ctx.globalAlpha = 1;
ctx.fillText(unit, ux + (uw - ctx.measureText(unit).width) / 2, cy + 1);
ctx.restore();
// Décoration coin
drawPaintSplash(ctx, w - 12, h - 8, 10, PAINT, 0.06);
ctx.save(); ctx.strokeStyle = COLOR1; ctx.globalAlpha = 0.15; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.moveTo(w - 30, 0); ctx.lineTo(w + 2, h);
ctx.stroke(); ctx.restore();
};
// ============================================================
// SCENE_MENU — layout + création fenêtres
// ============================================================
Scene_Menu.prototype.commandWindowRect = function () {
const ww = Math.floor(Graphics.boxWidth * 0.42);
const wh = this.mainAreaHeight() - GOLD_H - 4;
return new Rectangle(0, this.mainAreaTop(), ww, wh);
};
Scene_Menu.prototype.statusWindowRect = function () {
const cx = Math.floor(Graphics.boxWidth * 0.42) + 4;
return new Rectangle(cx, this.mainAreaTop(),
Graphics.boxWidth - cx, this.mainAreaHeight());
};
Scene_Menu.prototype.goldWindowRect = function () {
const ww = Math.floor(Graphics.boxWidth * 0.42);
const wy = this.mainAreaBottom() - GOLD_H;
return new Rectangle(0, wy, ww, GOLD_H);
};
Scene_Menu.prototype.createCommandWindow = function () {
const rect = this.commandWindowRect();
this._commandWindow = new Window_UniqueMenuCommand(rect);
this._commandWindow.setHandler('item', this.commandItem.bind(this));
this._commandWindow.setHandler('skill', this.commandPersonal.bind(this));
this._commandWindow.setHandler('equip', this.commandPersonal.bind(this));
this._commandWindow.setHandler('status', this.commandPersonal.bind(this));
this._commandWindow.setHandler('formation', this.commandFormation.bind(this));
this._commandWindow.setHandler('options', this.commandOptions.bind(this));
this._commandWindow.setHandler('save', this.commandSave.bind(this));
this._commandWindow.setHandler('gameEnd', this.commandGameEnd.bind(this));
this._commandWindow.setHandler('cancel', this._startCloseTransition.bind(this));
this.addWindow(this._commandWindow);
};
Scene_Menu.prototype.createStatusWindow = function () {
this._statusWindow = new Window_UniqueMenuStatus(this.statusWindowRect());
this.addWindow(this._statusWindow);
};
const _createGold = Scene_Menu.prototype.createGoldWindow;
Scene_Menu.prototype.createGoldWindow = function () {
_createGold.call(this);
if (this._goldWindow) { this._goldWindow.opacity = 0; this._goldWindow.frameVisible = false; }
};
// Supprimer "^:Select / X:Back" — on cache la fenêtre d'info de formation
const _Scene_Menu_create = Scene_Menu.prototype.create;
Scene_Menu.prototype.create = function () {
_Scene_Menu_create.call(this);
// Masquer toute fenêtre d'info/help éventuellement créée
if (this._helpWindow) {
this._helpWindow.opacity = 0;
this._helpWindow.frameVisible = false;
this._helpWindow.visible = false;
}
};
// Empêcher la mise à jour du texte d'aide (^:Select, X:Back)
Window_UniqueMenuStatus.prototype.updateHelp = function () {};
// ============================================================
// TRANSITIONS MENU — Sweep SF6 stylisé + slide des fenêtres
// ============================================================
const _Scene_Menu_start = Scene_Menu.prototype.start;
Scene_Menu.prototype.start = function () {
_Scene_Menu_start.call(this);
this._menuState = 'opening'; // 'opening' | 'idle' | 'closing'
this._transTimer = 0;
// Positions de départ pour le slide
this._commandWindow.x = -this._commandWindow.width;
this._statusWindow.x = Graphics.boxWidth;
this._goldWindow.y = Graphics.boxHeight;
// Sprite de sweep par-dessus tout
this._sweepSprite = new Sprite(new Bitmap(Graphics.width, Graphics.height));
this._sweepSprite.z = 100;
this.addChild(this._sweepSprite);
this._drawSweepOpen(0);
};
// Sweep d'ouverture : rideau rouge qui part de la gauche
Scene_Menu.prototype._drawSweepOpen = function (progress) {
if (!this._sweepSprite) return;
const bmp = this._sweepSprite.bitmap; bmp.clear();
const ctx = getCtx(bmp); if (!ctx) return;
const W = Graphics.width, H = Graphics.height;
const sweepX = W * progress;
// Flash blanc très bref au tout début (impact)
if (progress < 0.08) {
ctx.save(); ctx.fillStyle = '#ffffff';
ctx.globalAlpha = (0.08 - progress) / 0.08 * 0.6;
ctx.fillRect(0, 0, W, H); ctx.restore();
}
// Rideau principal rouge qui recule vers la gauche
if (sweepX < W) {
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(0, 0); ctx.lineTo(sweepX + 50, 0);
ctx.lineTo(sweepX - 25, H); ctx.lineTo(0, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Bande sombre derrière le rideau (épaisseur du bord)
ctx.save(); ctx.fillStyle = '#000'; ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.moveTo(sweepX - 10, 0); ctx.lineTo(sweepX + 50, 0);
ctx.lineTo(sweepX - 5, H); ctx.lineTo(sweepX - 50, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Ligne lumineuse sur le bord tranchant
ctx.save(); ctx.strokeStyle = PAINT; ctx.lineWidth = 3; ctx.globalAlpha = 0.9;
ctx.beginPath();
ctx.moveTo(sweepX + 50, 0); ctx.lineTo(sweepX - 25, H);
ctx.stroke(); ctx.restore();
}
// Scanlines sur le rideau (style rétro)
if (sweepX > 10) {
ctx.save(); ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.globalAlpha = 0.12;
for (let y = 0; y < H; y += 4) {
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(Math.min(sweepX + 50, W), y);
ctx.stroke();
}
ctx.restore();
}
// Splashes sur le bord tranchant
if (progress > 0.05 && progress < 0.95) {
for (let i = 0; i < 6; i++) {
drawPaintSplash(ctx,
sweepX + (Math.random() - 0.5) * 70,
(i / 5) * H + (Math.random() - 0.5) * 60,
8 + Math.random() * 22, PAINT,
0.55 * (1 - progress));
}
}
// Texte "MENU" avec double ombre style P5
if (progress < 0.65) {
const label = 'MENU';
const alpha = Math.max(0, (0.65 - progress) / 0.65);
const centerX = sweepX * 0.45;
ctx.save();
ctx.font = 'bold 72px "Arial Black", Arial, sans-serif';
ctx.textBaseline = 'middle'; ctx.textAlign = 'center';
// Ombre rouge décalée (effet P5 signature)
ctx.fillStyle = COLOR1; ctx.globalAlpha = alpha * 0.8;
ctx.fillText(label, centerX + 6, H / 2 + 6);
// Ombre dorée
ctx.fillStyle = PAINT; ctx.globalAlpha = alpha * 0.6;
ctx.fillText(label, centerX + 3, H / 2 + 3);
// Texte principal blanc
ctx.fillStyle = ACCENT; ctx.globalAlpha = alpha;
ctx.fillText(label, centerX, H / 2);
ctx.restore();
}
};
// Sweep de fermeture : rideau rouge vient de la droite
Scene_Menu.prototype._drawSweepClose = function (progress) {
if (!this._sweepSprite) return;
const bmp = this._sweepSprite.bitmap; bmp.clear();
const ctx = getCtx(bmp); if (!ctx) return;
const W = Graphics.width, H = Graphics.height;
const sweepX = W * (1 - progress); // bord gauche du rideau, avance de droite à gauche
// Rideau principal rouge
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(sweepX - 50, 0); ctx.lineTo(W, 0);
ctx.lineTo(W, H); ctx.lineTo(sweepX + 25, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Bande sombre sur le bord avant (impact visuel)
ctx.save(); ctx.fillStyle = '#000'; ctx.globalAlpha = 0.45;
ctx.beginPath();
ctx.moveTo(sweepX - 50, 0); ctx.lineTo(sweepX + 10, 0);
ctx.lineTo(sweepX + 50, H); ctx.lineTo(sweepX - 10, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Ligne lumineuse sur le bord
ctx.save(); ctx.strokeStyle = PAINT; ctx.lineWidth = 3; ctx.globalAlpha = 0.85;
ctx.beginPath();
ctx.moveTo(sweepX - 50, 0); ctx.lineTo(sweepX + 25, H);
ctx.stroke(); ctx.restore();
// Scanlines
ctx.save(); ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.globalAlpha = 0.1;
for (let y = 0; y < H; y += 4) {
ctx.beginPath();
ctx.moveTo(Math.max(0, sweepX - 50), y); ctx.lineTo(W, y);
ctx.stroke();
}
ctx.restore();
// Splashes sur le bord
if (progress > 0.05 && progress < 0.95) {
for (let i = 0; i < 6; i++) {
drawPaintSplash(ctx,
sweepX + (Math.random() - 0.5) * 70,
(i / 5) * H + (Math.random() - 0.5) * 60,
8 + Math.random() * 22, PAINT,
0.55 * progress);
}
}
// Texte "MENU" avec triple ombre style P5
if (progress > 0.40) {
const label = 'MENU';
const alpha = Math.min(1, (progress - 0.40) / 0.4);
const cx = sweepX + (W - sweepX) * 0.5;
ctx.save();
ctx.font = 'bold 72px "Arial Black", Arial, sans-serif';
ctx.textBaseline = 'middle'; ctx.textAlign = 'center';
ctx.fillStyle = COLOR1; ctx.globalAlpha = alpha * 0.8;
ctx.fillText(label, cx + 6, H / 2 + 6);
ctx.fillStyle = PAINT; ctx.globalAlpha = alpha * 0.6;
ctx.fillText(label, cx + 3, H / 2 + 3);
ctx.fillStyle = ACCENT; ctx.globalAlpha = alpha;
ctx.fillText(label, cx, H / 2);
ctx.restore();
}
// Flash blanc à la fin (écran couvert)
if (progress > 0.92) {
ctx.save(); ctx.fillStyle = '#fff';
ctx.globalAlpha = (progress - 0.92) / 0.08 * 0.35;
ctx.fillRect(0, 0, W, H); ctx.restore();
}
};
Scene_Menu.prototype._startCloseTransition = function () {
if (this._menuState === 'closing') return;
this._menuState = 'closing';
this._transTimer = 0;
if (!this._sweepSprite) {
this._sweepSprite = new Sprite(new Bitmap(Graphics.width, Graphics.height));
this._sweepSprite.z = 100;
this.addChild(this._sweepSprite);
}
};
const _Scene_Menu_update = Scene_Menu.prototype.update;
Scene_Menu.prototype.update = function () {
_Scene_Menu_update.call(this);
this._transTimer++;
const speed = TRANS_SPEED;
if (this._menuState === 'opening') {
const p = Math.min(1, this._transTimer / speed);
this._drawSweepOpen(p);
// Slide des fenêtres avec easing
const ease = t => 1 - Math.pow(1 - t, 3); // ease out cubic
const ep = ease(p);
const cmdTargetX = 0;
const stTargetX = Math.floor(Graphics.boxWidth * 0.42) + 4;
const gdTargetY = this.mainAreaBottom() - GOLD_H;
this._commandWindow.x = Math.round(-this._commandWindow.width * (1 - ep));
this._statusWindow.x = Math.round(Graphics.boxWidth - (Graphics.boxWidth - stTargetX) * ep);
this._goldWindow.y = Math.round(Graphics.boxHeight - GOLD_H * ep);
if (p >= 1) {
this._menuState = 'idle';
this._commandWindow.x = cmdTargetX;
this._statusWindow.x = stTargetX;
this._goldWindow.y = gdTargetY;
this.removeChild(this._sweepSprite);
this._sweepSprite = null;
}
} else if (this._menuState === 'closing') {
const p = Math.min(1, this._transTimer / speed);
this._drawSweepClose(p);
if (p >= 1) {
this._menuState = 'done';
this.popScene();
}
}
};
// ============================================================
// SWEEP SOUS-SCÈNES (Items, Skill, Equip, Status, Save…)
// ============================================================
function addSceneSweep(SceneClass) {
const _start = SceneClass.prototype.start;
const _update = SceneClass.prototype.update;
SceneClass.prototype.start = function () {
_start.call(this);
this._subTimer = 0; this._subDone = false;
this._subSprite = new Sprite(new Bitmap(Graphics.width, Graphics.height));
this._subSprite.z = 100;
this.addChild(this._subSprite);
_drawSubSweep(this._subSprite, 0);
};
SceneClass.prototype.update = function () {
_update.call(this);
if (this._subSprite && !this._subDone) {
this._subTimer++;
const p = Math.min(1, this._subTimer / Math.round(TRANS_SPEED * 1.1));
_drawSubSweep(this._subSprite, p);
if (p >= 1) {
this._subDone = true;
this.removeChild(this._subSprite); this._subSprite = null;
}
}
};
}
function _drawSubSweep(sprite, progress) {
const bmp = sprite.bitmap; bmp.clear();
const ctx = getCtx(bmp); if (!ctx) return;
const W = Graphics.width, H = Graphics.height;
const sweepX = W * progress;
// Flash bref au début
if (progress < 0.06) {
ctx.save(); ctx.fillStyle = COLOR1;
ctx.globalAlpha = (0.06 - progress) / 0.06 * 0.5;
ctx.fillRect(0, 0, W, H); ctx.restore();
}
// Rideau sombre principal
ctx.save(); ctx.fillStyle = COLOR2; ctx.globalAlpha = 0.97;
ctx.beginPath();
ctx.moveTo(0, 0); ctx.lineTo(sweepX + 35, 0);
ctx.lineTo(sweepX - 18, H); ctx.lineTo(0, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Bande rouge sur le bord tranchant
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 0.9;
ctx.beginPath();
ctx.moveTo(sweepX + 20, 0); ctx.lineTo(sweepX + 35, 0);
ctx.lineTo(sweepX - 4, H); ctx.lineTo(sweepX - 18, H);
ctx.closePath(); ctx.fill(); ctx.restore();
// Ligne lumineuse PAINT
ctx.save(); ctx.strokeStyle = PAINT; ctx.lineWidth = 2; ctx.globalAlpha = 0.7;
ctx.beginPath();
ctx.moveTo(sweepX + 35, 0); ctx.lineTo(sweepX - 18, H);
ctx.stroke(); ctx.restore();
// Barre rouge verticale à gauche
ctx.save(); ctx.fillStyle = COLOR1; ctx.globalAlpha = 1;
ctx.fillRect(0, 0, 5, H * Math.max(0, 1.05 - progress));
ctx.restore();
// Splashes
if (progress > 0.05 && progress < 0.90) {
for (let i = 0; i < 4; i++) {
drawPaintSplash(ctx,
sweepX + (Math.random() - 0.5) * 50,
(i / 3) * H + (Math.random() - 0.5) * 50,
5 + Math.random() * 14, PAINT,
0.45 * (1 - progress));
}
}
}
[Scene_Item, Scene_Skill, Scene_Equip, Scene_Status,
Scene_Save, Scene_Options, Scene_GameEnd].forEach(addSceneSweep);
// ============================================================
// FORMATION — corrigée et fonctionnelle
// ============================================================
Scene_Menu.prototype.commandFormation = function () {
this._commandWindow.deactivate();
this._statusWindow.setFormationMode(true);
this._statusWindow.selectLast();
this._statusWindow.activate();
this._statusWindow.setHandler('ok', this.onFormationOk.bind(this));
this._statusWindow.setHandler('cancel', this.onFormationCancel.bind(this));
};
Scene_Menu.prototype.onFormationOk = function () {
const index = this._statusWindow.index();
const pendingIndex = this._statusWindow.pendingIndex();
if (pendingIndex >= 0) {
$gameParty.swapOrder(index, pendingIndex);
this._statusWindow.setPendingIndex(-1);
this._statusWindow.redrawItem(index);
this._statusWindow.redrawItem(pendingIndex);
this._statusWindow.activate();
} else {
this._statusWindow.setPendingIndex(index);
this._statusWindow.activate();
}
};
Scene_Menu.prototype.onFormationCancel = function () {
if (this._statusWindow.pendingIndex() >= 0) {
this._statusWindow.setPendingIndex(-1);
this._statusWindow.activate();
} else {
this._statusWindow.deactivate();
this._statusWindow.setFormationMode(false);
this._commandWindow.activate();
}
};
console.log('[UniqueMenu_MZ v1.7] Transitions stylisées + Formation corrigée');
// PATCH — GOLD ULTRA VISIBLE
Window_Gold.prototype.refresh = function () {
if (!this.contents) return;
this.contents.clear();
const ctx = this.contents.context;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond très contrasté
ctx.save();
const gradBg = ctx.createLinearGradient(0, 0, w, h);
gradBg.addColorStop(0, "#3A2A00");
gradBg.addColorStop(1, "#1A1200");
ctx.fillStyle = gradBg;
ctx.globalAlpha = 0.95;
ctx.fillRect(0, 0, w, h);
ctx.restore();
// Bande dorée intense
ctx.save();
const barGrad = ctx.createLinearGradient(0, 0, w, 0);
barGrad.addColorStop(0, "#FFD700");
barGrad.addColorStop(1, "#FFEA8A");
ctx.fillStyle = barGrad;
ctx.fillRect(0, 0, w, 10);
ctx.restore();
// Badge GOLD très visible
ctx.save();
ctx.fillStyle = COLOR1;
ctx.globalAlpha = 1;
ctx.fillRect(10, 14, 90, 28);
ctx.font = 'bold 15px "Arial Black", Arial';
ctx.fillStyle = ACCENT;
ctx.textBaseline = 'middle';
ctx.fillText('GOLD', 20, 28);
ctx.restore();
// Icône pièce — version brillante
const cy = h / 2 + 10;
const iconR = 22;
const iconX = 120;
ctx.save();
const coinGrad = ctx.createRadialGradient(iconX - 6, cy - 6, 1, iconX, cy, iconR);
coinGrad.addColorStop(0, "#FFFDE7");
coinGrad.addColorStop(0.4, "#FFD700");
coinGrad.addColorStop(1, "#8B6B00");
ctx.beginPath();
ctx.arc(iconX, cy, iconR, 0, Math.PI * 2);
ctx.fillStyle = coinGrad;
ctx.fill();
// Contour néon
ctx.strokeStyle = "#FFF8B0";
ctx.lineWidth = 3;
ctx.globalAlpha = 0.9;
ctx.stroke();
// Lettre G
ctx.fillStyle = "#1A1A1A";
ctx.font = 'bold 17px "Arial Black", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('G', iconX, cy + 1);
ctx.restore();
// Valeur — énorme + halo
const value = $gameParty.gold().toLocaleString();
const unit = TextManager.currencyUnit;
const startX = iconX + iconR + 20;
ctx.save();
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
// Halo
ctx.font = 'bold 40px "Arial Black", Arial';
ctx.fillStyle = PAINT;
ctx.globalAlpha = 0.25;
ctx.fillText(value, startX + 4, cy + 4);
// Texte principal
ctx.globalAlpha = 1;
ctx.fillStyle = PAINT;
ctx.fillText(value, startX, cy);
// Badge unité
const vw = ctx.measureText(value).width;
const ux = startX + vw + 20;
const uw = ctx.measureText(unit).width + 22;
const uh = 34;
ctx.fillStyle = COLOR1;
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.moveTo(ux + 8, cy - uh / 2);
ctx.lineTo(ux + uw + 8, cy - uh / 2);
ctx.lineTo(ux + uw - 8, cy + uh / 2);
ctx.lineTo(ux - 8, cy + uh / 2);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = PAINT;
ctx.lineWidth = 2;
ctx.globalAlpha = 0.8;
ctx.stroke();
ctx.fillStyle = ACCENT;
ctx.globalAlpha = 1;
ctx.fillText(unit, ux + (uw - ctx.measureText(unit).width) / 2, cy + 1);
ctx.restore();
};
// PATCH — GOLD FIX (remove red badge + move money up)
Window_Gold.prototype.refresh = function () {
if (!this.contents) return;
this.contents.clear();
const ctx = this.contents.context;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond sombre propre
drawBg(ctx, w, h, {
barSide: 'top',
barSize: 6,
barColor: PAINT,
speedLines: false,
bgAlpha: 0.92
});
// Position verticale corrigée
const cy = Math.floor(h / 2); // centré verticalement
// Icône pièce
const iconR = 20;
const iconX = 20 + iconR;
ctx.save();
const grad = ctx.createRadialGradient(iconX - 4, cy - 4, 1, iconX, cy, iconR);
grad.addColorStop(0, "#FFFDE7");
grad.addColorStop(0.4, "#FFD700");
grad.addColorStop(1, "#8B6B00");
ctx.beginPath();
ctx.arc(iconX, cy, iconR, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
ctx.strokeStyle = "#FFF8B0";
ctx.lineWidth = 2;
ctx.globalAlpha = 0.9;
ctx.stroke();
ctx.fillStyle = "#1A1A1A";
ctx.font = 'bold 15px "Arial Black", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('G', iconX, cy + 1);
ctx.restore();
// Montant
const value = $gameParty.gold().toLocaleString();
const unit = TextManager.currencyUnit;
const startX = iconX + iconR + 16;
ctx.save();
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
ctx.font = 'bold 26px "Arial Black", Arial';
ctx.fillStyle = PAINT;
ctx.globalAlpha = 1;
ctx.fillText(value, startX, cy);
// Unité
const vw = ctx.measureText(value).width;
ctx.font = 'bold 16px "Arial Black", Arial';
ctx.fillStyle = COLOR1;
ctx.globalAlpha = 0.9;
ctx.fillText(unit, startX + vw + 12, cy);
ctx.restore();
};
// PATCH — GOLD FIX v2 (higher + styled unit)
Window_Gold.prototype.refresh = function () {
if (!this.contents) return;
this.contents.clear();
const ctx = this.contents.context;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond sombre propre
drawBg(ctx, w, h, {
barSide: 'top',
barSize: 6,
barColor: PAINT,
speedLines: false,
bgAlpha: 0.92
});
// Position verticale remontée
const cy = Math.floor(h / 2) - 6;
// Icône pièce
const iconR = 15;
const iconX = 15 + iconR;
ctx.save();
const grad = ctx.createRadialGradient(iconX - 4, cy - 4, 1, iconX, cy, iconR);
grad.addColorStop(0, "#FFFDE7");
grad.addColorStop(0.4, "#FFD700");
grad.addColorStop(1, "#8B6B00");
ctx.beginPath();
ctx.arc(iconX, cy, iconR, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
ctx.strokeStyle = "#FFF8B0";
ctx.lineWidth = 2;
ctx.globalAlpha = 0.9;
ctx.stroke();
ctx.fillStyle = "#1A1A1A";
ctx.font = 'bold 15px "Arial Black", Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('G', iconX, cy + 1);
ctx.restore();
// Montant
const value = $gameParty.gold().toLocaleString();
const unit = TextManager.currencyUnit;
const startX = iconX + iconR + 16;
ctx.save();
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
ctx.font = 'bold 26px "Arial Black", Arial';
ctx.fillStyle = PAINT;
ctx.globalAlpha = 1;
ctx.fillText(value, startX, cy);
// Badge unité stylé (Mo)
const vw = ctx.measureText(value).width;
const ux = startX + vw + 16;
const uw = ctx.measureText(unit).width + 22;
const uh = 26;
drawSkewedRect(ctx, ux, cy - uh / 2, uw, uh, 6, COLOR1, 0.95);
ctx.font = 'bold 14px "Arial Black", Arial';
ctx.fillStyle = ACCENT;
ctx.globalAlpha = 1;
ctx.fillText(unit, ux + (uw - ctx.measureText(unit).width) / 2, cy + 1);
ctx.restore();
};
// PATCH — STATUS CLEANER (stats + équipement séparés)
Window_Status.prototype.refresh = function () {
if (!this.contents || !this._actor) return;
this.contents.clear();
const ctx = this.contents.context;
const actor = this._actor;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond stylisé
drawBg(ctx, w, h, { barSide: 'left', barSize: 6, speedLines: true });
// Portrait
const faceW = 128, faceH = 128;
this.drawFace(actor.faceName(), actor.faceIndex(), 14, 14, faceW, faceH);
// Cadres portrait
ctx.save();
ctx.strokeStyle = COLOR1; ctx.lineWidth = 3; ctx.globalAlpha = 0.9;
ctx.strokeRect(12, 12, faceW + 4, faceH + 4);
ctx.strokeStyle = PAINT; ctx.lineWidth = 1; ctx.globalAlpha = 0.4;
ctx.strokeRect(8, 8, faceW + 12, faceH + 12);
ctx.restore();
// Nom + classe + niveau
const infoX = faceW + 28;
p5Text(ctx, actor.name().toUpperCase(), infoX, 28, { size: 28 });
p5Text(ctx, actor.currentClass().name.toUpperCase(), infoX + 2, 60,
{ size: 14, color: COLOR1, shadow: false });
drawSkewedRect(ctx, infoX, 72, 100, 28, 7, COLOR1, 0.95);
p5Text(ctx, `LV. ${actor.level}`, infoX + 8, 86, { size: 16, color: '#000', shadow: false });
// HP / MP
const barW = w - infoX - 16;
const hpRatio = actor.hp / actor.mhp;
const mpRatio = actor.mp / actor.mmp;
p5Text(ctx, `HP ${actor.hp}/${actor.mhp}`, infoX, 120, { size: 14, color: PAINT });
drawStatBar(ctx, infoX, 132, barW, 10, hpRatio, '#00DD55');
p5Text(ctx, `MP ${actor.mp}/${actor.mmp}`, infoX, 158, { size: 14, color: '#5599FF' });
drawStatBar(ctx, infoX, 170, barW, 10, mpRatio, '#5599FF');
// Séparateur
ctx.save();
ctx.fillStyle = COLOR1; ctx.globalAlpha = 0.55;
ctx.fillRect(10, faceH + 22, w - 20, 2);
ctx.restore();
// ─────────────────────────────────────────────
// ZONE STATS (2 colonnes × 4 lignes)
// ─────────────────────────────────────────────
p5Text(ctx, "STATISTIQUES", 14, faceH + 32, { size: 14, color: PAINT });
const stats = [
{ label: 'ATK', val: actor.atk, color: '#FF6666' },
{ label: 'DEF', val: actor.def, color: '#66AAFF' },
{ label: 'M.ATK', val: actor.mat, color: '#CC66FF' },
{ label: 'M.DEF', val: actor.mdf, color: '#66CCFF' },
{ label: 'VITESSE', val: actor.agi, color: '#66FF99' },
{ label: 'CHANCE', val: actor.luk, color: PAINT },
{ label: 'HP MAX', val: actor.mhp, color: '#00DD55' },
{ label: 'MP MAX', val: actor.mmp, color: '#5599FF' },
];
const gridY = faceH + 50;
const colW = Math.floor((w - 40) / 2);
const rowH = 48;
stats.forEach((st, i) => {
const col = i % 2;
const row = Math.floor(i / 2);
const sx = 14 + col * colW;
const sy = gridY + row * rowH;
drawSkewedRect(ctx, sx, sy, colW - 12, rowH - 6, 6, ACCENT, 0.08);
p5Text(ctx, st.label, sx + 12, sy + 12, { size: 11, color: st.color, shadow: false });
p5Text(ctx, String(st.val), sx + 12, sy + 26, { size: 20, color: ACCENT });
});
// ─────────────────────────────────────────────
// ZONE ÉQUIPEMENT
// ─────────────────────────────────────────────
const eqY = gridY + rowH * 4 + 10;
p5Text(ctx, "ÉQUIPEMENT", 14, eqY, { size: 14, color: PAINT });
const equips = [
{ label: "Arme", item: actor.equips()[0] },
{ label: "Bouclier", item: actor.equips()[1] },
{ label: "Tête", item: actor.equips()[2] },
{ label: "Plastron", item: actor.equips()[3] },
{ label: "Accessoire", item: actor.equips()[4] },
];
let y = eqY + 20;
equips.forEach(eq => {
drawSkewedRect(ctx, 14, y, w - 28, 32, 6, ACCENT, 0.06);
p5Text(ctx, eq.label.toUpperCase(), 20, y + 16, { size: 11, color: COLOR1, shadow: false });
const name = eq.item ? eq.item.name : "—";
p5Text(ctx, name.toUpperCase(), 120, y + 16, { size: 14, color: ACCENT });
y += 36;
});
};
// PATCH — STATUS MODERNE SF6 (coins arrondis + glow rouge)
Window_Status.prototype.refresh = function () {
if (!this.contents || !this._actor) return;
this.contents.clear();
const ctx = this.contents.context;
const actor = this._actor;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond stylisé
drawBg(ctx, w, h, { barSide: 'left', barSize: 6, speedLines: true });
// ─────────────────────────────────────────────
// PORTRAIT + INFOS
// ─────────────────────────────────────────────
const faceW = 128, faceH = 128;
this.drawFace(actor.faceName(), actor.faceIndex(), 14, 14, faceW, faceH);
// Cadres portrait
ctx.save();
ctx.strokeStyle = COLOR1; ctx.lineWidth = 3; ctx.globalAlpha = 0.9;
ctx.strokeRect(12, 12, faceW + 4, faceH + 4);
// Cadres portrait
ctx.save();
ctx.strokeStyle = COLOR1;
ctx.lineWidth = 3;
ctx.globalAlpha = 0.9;
ctx.strokeRect(12, 12, faceW + 4, faceH + 4);
ctx.strokeStyle = PAINT;
ctx.lineWidth = 1;
ctx.globalAlpha = 0.4;
ctx.strokeRect(8, 8, faceW + 12, faceH + 12);
ctx.restore();
// PATCH — STATUS MODERNE SF6 (coins arrondis + glow rouge)
Window_Status.prototype.refresh = function () {
if (!this.contents || !this._actor) return;
this.contents.clear();
const ctx = this.contents.context;
const actor = this._actor;
const w = this.contentsWidth();
const h = this.contentsHeight();
// Fond stylisé
drawBg(ctx, w, h, { barSide: 'left', barSize: 6, speedLines: true });
// ─────────────────────────────────────────────
// PORTRAIT + INFOS
// ─────────────────────────────────────────────
const faceW = 128, faceH = 128;
this.drawFace(actor.faceName(), actor.faceIndex(), 14, 14, faceW, faceH);
// Cadres portrait
ctx.save();
ctx.strokeStyle = COLOR1;
ctx.lineWidth = 3;
ctx.globalAlpha = 0.9;
ctx.strokeRect(12, 12, faceW + 4, faceH + 4);
ctx.strokeStyle = PAINT;
ctx.lineWidth = 1;
ctx.globalAlpha = 0.4;
ctx.strokeRect(8, 8, faceW + 12, faceH + 12);
ctx.restore();
// Nom + classe + niveau
const infoX = faceW + 28;
p5Text(ctx, actor.name().toUpperCase(), infoX, 28, { size: 28 });
p5Text(ctx, actor.currentClass().name.toUpperCase(), infoX + 2, 60,
{ size: 14, color: COLOR1, shadow: false });
drawSkewedRect(ctx, infoX, 72, 100, 28, 7, COLOR1, 0.95);
p5Text(ctx, `LV. ${actor.level}`, infoX + 8, 86, { size: 16, color: '#000', shadow: false });
// ─────────────────────────────────────────────
// HP / MP / EXP — rangés proprement
// ─────────────────────────────────────────────
const barW = w - infoX - 20;
const hpRatio = actor.hp / actor.mhp;
const mpRatio = actor.mp / actor.mmp;
const expRate = actor.isMaxLevel() ? 1 :
(actor.currentExp() - actor.currentLevelExp()) /
(actor.nextLevelExp() - actor.currentLevelExp());
// HP
p5Text(ctx, `HP ${actor.hp}/${actor.mhp}`, infoX, 120, { size: 14, color: PAINT });
drawStatBar(ctx, infoX, 132, barW, 10, hpRatio, '#00DD55');
// MP
p5Text(ctx, `MP ${actor.mp}/${actor.mmp}`, infoX, 158, { size: 14, color: '#5599FF' });
drawStatBar(ctx, infoX, 170, barW, 10, mpRatio, '#5599FF');
// EXP
p5Text(ctx, `EXP ${Math.floor(expRate * 100)}%`, infoX, 196, { size: 12, color: PAINT });
drawStatBar(ctx, infoX, 208, barW, 8, expRate, PAINT);
// ─────────────────────────────────────────────
// BLOC STATISTIQUES — carré moderne + glow rouge
// ─────────────────────────────────────────────
const statsBoxX = 14;
const statsBoxY = 240;
const statsBoxW = w - 28;
const statsBoxH = 180;
ctx.save();
ctx.fillStyle = "rgba(0,0,0,0.35)";
ctx.strokeStyle = COLOR1;
ctx.lineWidth = 3;
ctx.shadowColor = COLOR1;
ctx.shadowBlur = 18;
ctx.beginPath();
ctx.roundRect(statsBoxX, statsBoxY, statsBoxW, statsBoxH, 10);
ctx.fill();
ctx.stroke();
ctx.restore();
const stats = [
{ label: 'ATK', val: actor.atk },
{ label: 'DEF', val: actor.def },
{ label: 'M.ATK', val: actor.mat },
{ label: 'M.DEF', val: actor.mdf },
{ label: 'AGILITÉ', val: actor.agi },
{ label: 'CHANCE', val: actor.luk },
{ label: 'HP MAX', val: actor.mhp },
{ label: 'MP MAX', val: actor.mmp },
];
const colW = Math.floor(statsBoxW / 2);
const rowH = 40;
stats.forEach((st, i) => {
const col = i % 2;
const row = Math.floor(i / 2);
const sx = statsBoxX + 12 + col * colW;
const sy = statsBoxY + 12 + row * rowH;
p5Text(ctx, st.label, sx, sy, { size: 12, color: COLOR1, shadow: false });
p5Text(ctx, String(st.val), sx, sy + 18, { size: 18, color: ACCENT });
});
// ─────────────────────────────────────────────
// ÉQUIPEMENT
// ─────────────────────────────────────────────
const eqY = statsBoxY + statsBoxH + 20;
const equips = [
{ label: "Arme", item: actor.equips()[0] },
{ label: "Bouclier", item: actor.equips()[1] },
{ label: "Tête", item: actor.equips()[2] },
{ label: "Plastron", item: actor.equips()[3] },
{ label: "Accessoire", item: actor.equips()[4] },
];
let y = eqY;
equips.forEach(eq => {
p5Text(ctx, eq.label.toUpperCase(), 14, y, { size: 11, color: COLOR1, shadow: false });
const name = eq.item ? eq.item.name : "—";
p5Text(ctx, name.toUpperCase(), 120, y, { size: 14, color: ACCENT });
y += 26;
});
};
// Désactiver les stats doublons par défaut
Window_Status.prototype.drawParameters = function () {};
})();
To embed this project on your website, copy the following code and paste it into your website's HTML: