/*:
* @target MZ
* @plugindesc [v1.1] Écran de victoire style Persona / SMT — Plein écran + fondu
* @author VictoryScreenPersona
*
* @param bgColor1
* @text Couleur de fond 1
* @type string
* @default #0a0a1a
*
* @param bgColor2
* @text Couleur de fond 2
* @type string
* @default #1a0a2e
*
* @param accentColor
* @text Couleur d'accent
* @type string
* @default #ff2d78
*
* @param victoryText
* @text Texte de victoire
* @type string
* @default VICTORY
*
* @param showEnemiesDefeated
* @text Afficher ennemis vaincus
* @type boolean
* @default true
*
* @param levelUpFlash
* @text Flash au Level Up
* @type boolean
* @default true
*
* @param fadeDuration
* @text Durée fondu retour (frames)
* @type number
* @min 10
* @max 120
* @default 45
* @desc Nombre de frames pour le fondu au noir vers la carte.
*
* @help
* ============================================================
* VictoryScreenPersona v1.1
* ============================================================
* Remplace COMPLÈTEMENT l'écran de victoire MZ.
* - Plein écran (suit le canvas PIXI de MZ)
* - Supprime la bulle de victoire native
* - Fondu au noir vers la carte
* ============================================================
*/
(() => {
"use strict";
const PLUGIN_NAME = "VictoryScreenPersona";
const params = PluginManager.parameters(PLUGIN_NAME);
const BG_COLOR1 = String(params.bgColor1 || "#0a0a1a");
const BG_COLOR2 = String(params.bgColor2 || "#1a0a2e");
const ACCENT_COLOR = String(params.accentColor || "#ff2d78");
const VICTORY_TEXT = String(params.victoryText || "VICTORY");
const SHOW_ENEMIES = String(params.showEnemiesDefeated) !== "false";
const LEVELUP_FLASH= String(params.levelUpFlash) !== "false";
const FADE_DUR = Number(params.fadeDuration || 45);
// ─────────────────────────────────────────────
// Utilitaires
// ─────────────────────────────────────────────
function hexToRgb(hex) {
return {
r: parseInt(hex.slice(1,3),16),
g: parseInt(hex.slice(3,5),16),
b: parseInt(hex.slice(5,7),16)
};
}
function rgb(r,g,b,a=1){ return `rgba(${r},${g},${b},${a})`; }
function acc(a=1){ const c=hexToRgb(ACCENT_COLOR); return rgb(c.r,c.g,c.b,a); }
// ─────────────────────────────────────────────
// Supprime la bulle native de victoire
// BattleManager.processVictory affiche le
// message via $gameMessage — on l'écrase.
// ─────────────────────────────────────────────
BattleManager.processVictory = function() {
// Gain de récompenses SANS afficher la fenêtre de message native
this.gainRewards();
this.endBattle(0);
};
// ─────────────────────────────────────────────
// Capture des récompenses juste AVANT gainRewards
// (les rewards existent encore ici)
// ─────────────────────────────────────────────
const _gainRewards = BattleManager.gainRewards;
BattleManager.gainRewards = function() {
// Snapshot AVANT que gainRewards efface/distribue
if (!BattleManager._personaSnapshot) {
BattleManager._personaSnapshot = {
exp: this._rewards ? this._rewards.exp : 0,
gold: this._rewards ? this._rewards.gold : 0,
enemies: $gameTroop.members().map(e => e.originalName()),
actors: $gameParty.battleMembers().map(a => ({
actorId: a.actorId(),
name: a.name(),
prevLevel: a.level,
prevExp: a.currentExp(),
faceName: a.faceName(),
faceIndex: a.faceIndex()
}))
};
// Sauvegarde niveaux avant gain
$gameParty.battleMembers().forEach(a => { a._snapshotLevel = a.level; });
}
_gainRewards.call(this);
};
// ─────────────────────────────────────────────
// Après endBattle(0) → on démarre notre scène
// ─────────────────────────────────────────────
const _endBattle = BattleManager.endBattle;
BattleManager.endBattle = function(result) {
if (result === 0 && BattleManager._personaSnapshot) {
const snap = BattleManager._personaSnapshot;
BattleManager._personaSnapshot = null;
// Calcule delta EXP APRÈS gainRewards
snap.actors = snap.actors.map(s => {
const actor = $gameActors.actor(s.actorId);
return Object.assign(s, {
newLevel: actor ? actor.level : s.prevLevel,
newExp: actor ? actor.currentExp() : s.prevExp
});
});
_endBattle.call(this, result);
// Remplace Scene_Map par notre scène
SceneManager.goto(Scene_VictoryPersona);
SceneManager._nextScene._snap = snap;
return;
}
_endBattle.call(this, result);
};
// ─────────────────────────────────────────────
// SCÈNE PRINCIPALE
// ─────────────────────────────────────────────
class Scene_VictoryPersona extends Scene_Base {
constructor() {
super();
this._snap = null;
this._phase = "flash";
this._timer = 0;
this._locked = true;
this._particles = [];
this._diagLines = [];
this._letters = [];
this._actorRows = [];
this._lvQueue = [];
this._curLv = null;
this._lvTimer = 0;
this._resultAlpha= 0;
this._flashAlpha = 1;
this._fadeAlpha = 0; // fondu sortie
this._canvas = null;
this._ctx = null;
}
// ── Création ──────────────────────────────
create() {
super.create();
this._buildCanvas();
this._initParticles(90);
this._initDiagLines(14);
this._initLetters();
this._buildActorRows();
}
_buildCanvas() {
// Récupère le vrai canvas PIXI pour coller dessus proprement
const pixiCanvas = document.querySelector("canvas");
this._canvas = document.createElement("canvas");
this._canvas.id = "persona-victory-canvas";
const resize = () => {
const ref = document.querySelector("canvas#persona-victory-canvas ~ canvas, canvas:not(#persona-victory-canvas)") || pixiCanvas;
const rect = (pixiCanvas || document.body).getBoundingClientRect();
this._canvas.width = pixiCanvas ? pixiCanvas.width : Graphics.width;
this._canvas.height = pixiCanvas ? pixiCanvas.height : Graphics.height;
this._canvas.style.position = "fixed";
this._canvas.style.left = pixiCanvas ? pixiCanvas.style.left || rect.left+"px" : "0";
this._canvas.style.top = pixiCanvas ? pixiCanvas.style.top || rect.top+"px" : "0";
this._canvas.style.width = pixiCanvas ? pixiCanvas.style.width || rect.width+"px" : "100%";
this._canvas.style.height = pixiCanvas ? pixiCanvas.style.height || rect.height+"px" : "100%";
this._canvas.style.zIndex = "9999";
this._canvas.style.imageRendering = "pixelated";
};
resize();
document.body.appendChild(this._canvas);
this._ctx = this._canvas.getContext("2d");
this._W = this._canvas.width;
this._H = this._canvas.height;
}
_buildActorRows() {
if (!this._snap) return;
this._actorRows = this._snap.actors.map(s => {
const actor = $gameActors.actor(s.actorId);
const gained = (s.newExp || s.prevExp) - s.prevExp;
const leveled = (s.newLevel || s.prevLevel) > s.prevLevel;
const curExp = actor ? (actor.currentExp() - actor.currentLevelExp()) : 0;
const maxExp = actor ? Math.max(1, actor.nextLevelExp() - actor.currentLevelExp()) : 1;
return {
name: s.name,
prevLevel: s.prevLevel,
newLevel: s.newLevel || s.prevLevel,
gained,
leveled,
bar: { cur: 0, target: Math.min(1, curExp / maxExp) },
done: false
};
});
this._lvQueue = this._actorRows.filter(r => r.leveled);
}
_initParticles(n) {
const c = hexToRgb(ACCENT_COLOR);
for (let i=0;i<n;i++) {
this._particles.push({
x: Math.random()*this._W,
y: Math.random()*this._H,
vx:(Math.random()-0.5)*0.5,
vy:-(Math.random()*0.9+0.2),
r: Math.random()*2.5+0.5,
a: Math.random()*0.5+0.2,
col: Math.random()<0.65 ? rgb(c.r,c.g,c.b,0.75) : "rgba(255,255,255,0.45)"
});
}
}
_initDiagLines(n) {
for (let i=0;i<n;i++) {
this._diagLines.push({
x: Math.random()*this._W*1.5 - this._W*0.25,
spd: Math.random()*1.2+0.4,
a: Math.random()*0.12+0.04,
w: Math.random()*1.8+0.3
});
}
}
_initLetters() {
this._letters = VICTORY_TEXT.split("").map((ch,i)=>({
ch, i,
oy: -(80 + i*20),
a: 0,
delay: i*5,
done: false
}));
}
// ── Update ────────────────────────────────
update() {
super.update();
this._timer++;
this._tick();
this._tickParticles();
this._tickDiagLines();
this._draw();
}
_tick() {
switch(this._phase) {
case "flash":
this._flashAlpha = Math.max(0, 1 - this._timer/20);
if (this._timer >= 20) this._phase = "title", this._timer = 0;
break;
case "title":
this._tickLetters();
if (this._timer > 70) this._phase = "results", this._timer = 0;
break;
case "results":
this._resultAlpha = Math.min(1, this._resultAlpha+0.05);
this._tickBars();
if (this._timer > 50) this._locked = false;
if (!this._locked && this._allBarsDone() && this._triggered()) {
if (this._lvQueue.length) {
this._phase = "levelup"; this._timer = 0;
this._locked = true;
} else {
this._phase = "fadeout"; this._timer = 0;
}
}
break;
case "levelup":
if (!this._curLv) { this._curLv = this._lvQueue.shift(); this._lvTimer = 0; }
this._lvTimer++;
if (this._lvTimer > 95) {
this._curLv = null;
if (!this._lvQueue.length) { this._locked = false; this._phase = "lvwait"; }
}
break;
case "lvwait":
if (this._triggered()) { this._phase = "fadeout"; this._timer = 0; }
break;
case "fadeout":
this._fadeAlpha = Math.min(1, this._timer / FADE_DUR);
if (this._fadeAlpha >= 1) this._phase = "done";
break;
case "done":
this._end();
break;
}
}
_tickLetters() {
this._letters.forEach(l => {
if (this._timer < l.delay) return;
const t = this._timer - l.delay;
l.oy = Math.max(0, l.oy + 9);
l.a = Math.min(1, t/8);
if (l.oy >= 0) l.done = true;
});
}
_tickBars() {
this._actorRows.forEach(r => {
if (!r.done) {
r.bar.cur = Math.min(r.bar.target, r.bar.cur + 0.009);
if (Math.abs(r.bar.cur - r.bar.target) < 0.004) {
r.bar.cur = r.bar.target; r.done = true;
}
}
});
}
_allBarsDone() { return this._actorRows.every(r => r.done); }
_triggered() {
return Input.isTriggered("ok") || Input.isTriggered("cancel") || TouchInput.isTriggered();
}
_tickParticles() {
this._particles.forEach(p => {
p.x += p.vx; p.y += p.vy;
if (p.y < -5) { p.y = this._H+5; p.x = Math.random()*this._W; }
});
}
_tickDiagLines() {
this._diagLines.forEach(l => {
l.x += l.spd;
if (l.x > this._W*1.3) l.x = -this._W*0.3;
});
}
// ── Dessin ────────────────────────────────
_draw() {
const ctx = this._ctx;
const W = this._W, H = this._H;
ctx.clearRect(0,0,W,H);
this._dBg(ctx,W,H);
this._dDiag(ctx,W,H);
this._dParticles(ctx);
this._dCorners(ctx,W,H);
if (this._phase !== "flash") this._dTitle(ctx,W,H);
if (["results","levelup","lvwait","fadeout"].includes(this._phase))
this._dResults(ctx,W,H);
if (this._phase === "levelup" && this._curLv)
this._dLevelUp(ctx,W,H);
// Fondu sortie
if (this._fadeAlpha > 0) {
ctx.fillStyle = `rgba(0,0,0,${this._fadeAlpha})`;
ctx.fillRect(0,0,W,H);
}
// Flash d'entrée
if (this._flashAlpha > 0) {
ctx.fillStyle = `rgba(255,255,255,${this._flashAlpha})`;
ctx.fillRect(0,0,W,H);
}
if (!this._locked && !["fadeout","done"].includes(this._phase))
this._dPrompt(ctx,W,H);
}
_dBg(ctx,W,H) {
const g = ctx.createLinearGradient(0,0,W,H);
g.addColorStop(0, BG_COLOR1);
g.addColorStop(1, BG_COLOR2);
ctx.fillStyle = g;
ctx.fillRect(0,0,W,H);
// Bande accent en bas
const c = hexToRgb(ACCENT_COLOR);
const bg = ctx.createLinearGradient(0,H-10,0,H);
bg.addColorStop(0, rgb(c.r,c.g,c.b,0));
bg.addColorStop(1, rgb(c.r,c.g,c.b,0.9));
ctx.fillStyle = bg;
ctx.fillRect(0,H-10,W,10);
}
_dDiag(ctx,W,H) {
const c = hexToRgb(ACCENT_COLOR);
ctx.save();
this._diagLines.forEach(l=>{
ctx.strokeStyle = rgb(c.r,c.g,c.b,l.a);
ctx.lineWidth = l.w;
ctx.beginPath();
ctx.moveTo(l.x,0);
ctx.lineTo(l.x - H*0.65, H);
ctx.stroke();
});
ctx.restore();
}
_dParticles(ctx) {
this._particles.forEach(p=>{
ctx.beginPath();
ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
ctx.fillStyle = p.col;
ctx.fill();
});
}
_dCorners(ctx,W,H) {
const c = hexToRgb(ACCENT_COLOR);
ctx.save();
ctx.strokeStyle = rgb(c.r,c.g,c.b,0.65);
ctx.lineWidth = 2;
const S = 44;
// coin haut-gauche
ctx.beginPath(); ctx.moveTo(18,18+S); ctx.lineTo(18,18); ctx.lineTo(18+S,18); ctx.stroke();
// coin haut-droit
ctx.beginPath(); ctx.moveTo(W-18-S,18); ctx.lineTo(W-18,18); ctx.lineTo(W-18,18+S); ctx.stroke();
// coin bas-gauche
ctx.beginPath(); ctx.moveTo(18,H-18-S); ctx.lineTo(18,H-18); ctx.lineTo(18+S,H-18); ctx.stroke();
// coin bas-droit
ctx.beginPath(); ctx.moveTo(W-18-S,H-18); ctx.lineTo(W-18,H-18); ctx.lineTo(W-18,H-18-S); ctx.stroke();
ctx.restore();
// Losange centré haut
this._diamond(ctx, W/2, 22, 7, acc(0.9));
}
_diamond(ctx,x,y,r,color) {
ctx.save(); ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(x,y-r); ctx.lineTo(x+r,y);
ctx.lineTo(x,y+r); ctx.lineTo(x-r,y);
ctx.closePath(); ctx.fill(); ctx.restore();
}
_dTitle(ctx,W,H) {
const baseY = 82;
const fsize = Math.round(H * 0.12); // adaptatif
ctx.save();
ctx.font = `900 ${fsize}px 'Segoe UI Black','Arial Black',Arial,sans-serif`;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
const totalW = ctx.measureText(VICTORY_TEXT).width;
const startX = (W - totalW) / 2;
const charW = totalW / VICTORY_TEXT.length;
this._letters.forEach((l,i) => {
ctx.globalAlpha = l.a;
const lx = startX + i * charW;
const ly = baseY + l.oy;
// Glow
ctx.shadowColor = acc(0.8);
ctx.shadowBlur = 28;
// Ombre décalée rosée
ctx.fillStyle = "rgba(255,180,180,0.25)";
ctx.fillText(l.ch, lx+3, ly+3);
// Texte blanc
ctx.fillStyle = "#ffffff";
ctx.fillText(l.ch, lx, ly);
// Underscore coloré
if (l.done) {
ctx.shadowBlur = 0;
ctx.fillStyle = acc(0.9);
const cw = ctx.measureText(l.ch).width;
ctx.fillRect(lx, ly + fsize*0.52, cw, 3);
}
});
ctx.globalAlpha = 1;
ctx.shadowBlur = 0;
ctx.restore();
}
_dResults(ctx,W,H) {
ctx.save();
ctx.globalAlpha = this._resultAlpha;
const c = hexToRgb(ACCENT_COLOR);
const px = 36, pw = W - 72;
const headerY = Math.round(H * 0.27);
// Séparateur
ctx.strokeStyle = acc(0.4);
ctx.lineWidth = 1;
ctx.beginPath(); ctx.moveTo(px,headerY); ctx.lineTo(px+pw,headerY); ctx.stroke();
// EXP
ctx.font = `bold ${Math.round(H*0.034)}px 'Segoe UI',Arial,sans-serif`;
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillStyle = "#ffffff";
const exp = this._snap ? this._snap.exp : 0;
const gold = this._snap ? this._snap.gold : 0;
ctx.fillText(`EXP +${exp}`, px+8, headerY+10);
// OR
ctx.textAlign = "right";
ctx.fillStyle = "#ffd700";
ctx.fillText(`OR +${gold}`, px+pw-8, headerY+10);
// Ennemis
if (SHOW_ENEMIES && this._snap && this._snap.enemies.length) {
ctx.textAlign = "center";
ctx.font = `${Math.round(H*0.026)}px 'Segoe UI',Arial,sans-serif`;
ctx.fillStyle = "rgba(255,255,255,0.45)";
const en = [...new Set(this._snap.enemies)].join(" · ");
ctx.fillText(en, W/2, headerY + Math.round(H*0.065));
}
// Acteurs
const rowStart = headerY + Math.round(H*0.12);
const rowH = Math.round(H*0.13);
this._actorRows.forEach((r,i) => {
this._dActorRow(ctx, r, px, rowStart + i*rowH, pw, rowH-6, c);
});
ctx.restore();
}
_dActorRow(ctx, data, x, y, w, h, c) {
// Fond
ctx.fillStyle = "rgba(255,255,255,0.04)";
this._rrect(ctx,x,y,w,h,5); ctx.fill();
const fs = Math.round(h*0.85);
const fpad = Math.round((h - fs)/2);
const faceX= x+8, faceY=y+fpad;
// Case face
ctx.fillStyle = "rgba(255,255,255,0.07)";
this._rrect(ctx,faceX,faceY,fs,fs,4); ctx.fill();
ctx.strokeStyle = acc(0.35); ctx.lineWidth=1;
this._rrect(ctx,faceX,faceY,fs,fs,4); ctx.stroke();
// Initiale
ctx.font = `900 ${Math.round(fs*0.55)}px 'Segoe UI Black','Arial Black',Arial,sans-serif`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#ffffff";
ctx.fillText(data.name[0].toUpperCase(), faceX+fs/2, faceY+fs/2);
const inX = faceX+fs+12;
const fnS = Math.round(h*0.22);
// Nom
ctx.font = `bold ${fnS}px 'Segoe UI',Arial,sans-serif`;
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillStyle = "#ffffff";
ctx.fillText(data.name, inX, y+Math.round(h*0.1));
// Niveau
ctx.font = `${Math.round(fnS*0.85)}px 'Segoe UI',Arial,sans-serif`;
ctx.fillStyle = data.leveled ? acc(1) : "rgba(255,255,255,0.55)";
const lvTxt = data.leveled ? `Lv ${data.prevLevel} → ${data.newLevel}` : `Lv ${data.newLevel}`;
ctx.fillText(lvTxt, inX, y+Math.round(h*0.42));
// Gain EXP (droite)
ctx.textAlign = "right";
ctx.font = `${Math.round(fnS*0.85)}px 'Segoe UI',Arial,sans-serif`;
ctx.fillStyle = "#aaffcc";
ctx.fillText(`+${data.gained} EXP`, x+w-10, y+Math.round(h*0.1));
// Barre EXP
const bX = inX, bY = y+Math.round(h*0.72);
const bW = x+w-inX-10, bH = Math.max(4, Math.round(h*0.1));
ctx.fillStyle = "rgba(255,255,255,0.1)";
this._rrect(ctx,bX,bY,bW,bH,bH/2); ctx.fill();
const fw = bW * data.bar.cur;
if (fw > 1) {
const bg = ctx.createLinearGradient(bX,0,bX+bW,0);
bg.addColorStop(0, acc(0.9));
bg.addColorStop(1,"rgba(255,255,255,0.95)");
ctx.fillStyle = bg;
ctx.shadowColor = acc(0.9);
ctx.shadowBlur = 7;
this._rrect(ctx,bX,bY,fw,bH,bH/2); ctx.fill();
ctx.shadowBlur = 0;
}
}
_dLevelUp(ctx,W,H) {
const t = this._lvTimer;
const c = hexToRgb(ACCENT_COLOR);
const cx= W/2, cy=H/2;
if (t<12 && LEVELUP_FLASH) {
ctx.fillStyle=`rgba(255,255,255,${0.85-t*0.07})`;
ctx.fillRect(0,0,W,H);
}
const ov = Math.min(0.78, t/14*0.78);
ctx.fillStyle=`rgba(0,0,8,${ov})`; ctx.fillRect(0,0,W,H);
if (t>6) {
const ra = Math.min(0.28,(t-6)/18*0.28);
ctx.save(); ctx.translate(cx,cy); ctx.rotate(t*0.004);
for(let i=0;i<12;i++){
const ang=(i/12)*Math.PI*2;
ctx.beginPath(); ctx.moveTo(0,0);
ctx.lineTo(Math.cos(ang)*H*0.55,Math.sin(ang)*H*0.55);
ctx.strokeStyle=`rgba(255,200,50,${ra})`;
ctx.lineWidth=20; ctx.stroke();
}
ctx.restore();
const cr=H*0.22+Math.sin(t*0.1)*8;
const rg=ctx.createRadialGradient(cx,cy,0,cx,cy,cr);
rg.addColorStop(0,`rgba(255,210,90,${Math.min(0.45,(t-6)/18*0.45)})`);
rg.addColorStop(1,"rgba(255,210,90,0)");
ctx.fillStyle=rg; ctx.beginPath(); ctx.arc(cx,cy,cr,0,Math.PI*2); ctx.fill();
}
const ta = t<15?t/15 : t>78?Math.max(0,1-(t-78)/17) : 1;
const ts = t<15?0.55+0.45*(t/15):1;
ctx.save();
ctx.globalAlpha=ta; ctx.translate(cx,cy); ctx.scale(ts,ts);
const fS1=Math.round(H*0.04);
ctx.font=`bold ${fS1}px 'Segoe UI',Arial,sans-serif`;
ctx.textAlign="center"; ctx.textBaseline="middle";
ctx.fillStyle="rgba(255,255,255,0.9)";
ctx.fillText(this._curLv.name,0,-H*0.1);
const fS2=Math.round(H*0.115);
ctx.shadowColor="#ffd700"; ctx.shadowBlur=28;
ctx.font=`900 ${fS2}px 'Segoe UI Black','Arial Black',Arial,sans-serif`;
ctx.fillStyle="#ffffff";
ctx.fillText("LEVEL UP!",0,0);
ctx.shadowBlur=14; ctx.shadowColor="#ffd700";
ctx.font=`bold ${Math.round(fS2*0.55)}px 'Segoe UI',Arial,sans-serif`;
ctx.fillStyle="#ffd700";
ctx.fillText(`Lv.${this._curLv.newLevel}`,0,H*0.1);
ctx.shadowBlur=0;
const dOff=H*0.18;
this._diamond(ctx,-dOff,0,11,acc(0.9));
this._diamond(ctx, dOff,0,11,acc(0.9));
this._diamond(ctx,0,-H*0.16,7,"rgba(255,220,100,0.9)");
this._diamond(ctx,0, H*0.16,7,"rgba(255,220,100,0.9)");
ctx.restore();
}
_dPrompt(ctx,W,H) {
const a = 0.35+0.35*Math.sin(Date.now()/420);
ctx.save();
ctx.globalAlpha=a;
ctx.font=`${Math.round(H*0.027)}px 'Segoe UI',Arial,sans-serif`;
ctx.textAlign="center"; ctx.textBaseline="bottom";
ctx.fillStyle="rgba(255,255,255,0.85)";
ctx.fillText("Appuyer pour continuer",W/2,H-18);
ctx.restore();
}
_rrect(ctx,x,y,w,h,r) {
ctx.beginPath();
ctx.moveTo(x+r,y);
ctx.lineTo(x+w-r,y); ctx.quadraticCurveTo(x+w,y,x+w,y+r);
ctx.lineTo(x+w,y+h-r); ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h);
ctx.lineTo(x+r,y+h); ctx.quadraticCurveTo(x,y+h,x,y+h-r);
ctx.lineTo(x,y+r); ctx.quadraticCurveTo(x,y,x+r,y);
ctx.closePath();
}
// ── Fin ───────────────────────────────────
_end() {
if (this._canvas) { this._canvas.remove(); this._canvas=null; }
SceneManager.goto(Scene_Map);
}
terminate() {
super.terminate();
if (this._canvas && this._canvas.parentNode) { this._canvas.remove(); this._canvas=null; }
}
}
// Expose globalement pour que SceneManager puisse instancier
window.Scene_VictoryPersona = Scene_VictoryPersona;
})();
To embed this project on your website, copy the following code and paste it into your website's HTML: