Sphinx-Spinner
Comme tous mes plugins, ce script est publié sous licence CC BY 4.0.
Récemment, je me suis retrouvé confronté à un dilemme. Je souhaitais (et je souhaite toujours) adapter un jeu de rôle des années 80 en jeu vidéo. Comme tous les jdr que je connais, celui-ci se joue au dés. Mais voilà, comment lancer un dé dans RPG Maker MV ? Bien sur, on peut toujours afficher une des 6 faces du dé aléatoirement. Mais le côté visuel ne me convenait pas. Et faire une projection 3D dans des sprites 2D, je ne sais pas faire.
Aussi j’ai décidé de prendre le problème autrement, au moyen d’une roulette. Celle-ci prend une liste de valeurs à sa construction, les affiche sur une roue qui se met à tourner jusqu’à ce que le joueur clique sur la roue pour l’arrêter.

Et en mode automatique (car oui, vous pouvez lancer un spinner qui s’arrêtera tout seul !) :

Installation
L’installation est assez simple finalement. Elle consiste en l’ajout à votre projet de ces deux plugins à placer dans l’ordre suivant.
Le premier, c’est mon polyfill. Si vous l’aviez déjà, mettez-le à jour, car j’ai ajouté une fonction qui est indispensable pour la suite depuis. Il est disponible ici : https://genie23.fr/js-rmmv/sphinx-polyfill/.
Le second, c’est le spinner à proprement parler :
//=============================================================================
// Sphinx-Spinner.js
//=============================================================================
/*:
* @plugindesc Graphique sous forme de sprite
* @author Sphinx
* @help
* //==========================================================================
* // Plugin : Sphinx-Spinner
* // Date : 7 janvier 2020
* // Auteur : Sphinx
* //==========================================================================
* Ce plugin permet de créer facilement un spinner comportant des valeurs
* personnalisées au moyen de la nouvelle classe SphinxSpinner.
*
* Usage :
* spinner = new SphinxSpinner({
* width: 640, // Largeur du spinner
* height: 480, // Hauteur du spinner
* values: [ "Pile", "Face" ], // Valeurs à afficher dans la roue
* colors: [ "red", "blue" ], // Couleurs CSS du dégradé de la roue
* speed: {
* start: 1, // Vitesse initiale sur une échelle de 1 à 100
* max: 50, // Vitesse finale sur une échelle de 1 à 100
* acceleration: 5 // Pourcentage d'accélération de la roue
* },
* auto: false, // Arrêt automatique de la roue (le joueur ne choisit pas
* // quand elle s'arrête)
* });
*
* Une fois le spinner créé, deux options se présentent :
* - Soit vous surveillez dans la méthode update de votre scène si le
* spinner s'est arrêté au moyen de la fonction :
* spinner.isStopped()
* puis vous récupérez l'index de sa valeur par le biais la propriété :
* spinner.value
* - Soit vous ajoutez un listener qui sera exécuté une et une seule fois
* lorsque le spinner s'arrête :
* spinner.setStopHandler(callback);
* L'index de la valeur du spinner sera passée à la fonction de callback.
* /!\ Si vous passez le contexte à la fonction de callback au moyen
* de la méthode callback.bind(this); pensez à passer aussi la référence
* au spinner au moyen de callback.bind(this, spinner); pour pouvoir
* récupérer la valeur du spinner.
*
* Outre le spinner, la classe SphinxSpinner intègre une méthode statique qui
* permet de récupérer un objet bitmap correspondant à l'ID d'une icône :
* bitmap = SphinxSpinner.getIcon(id);
*
* Dépendances : - Sphinx-Polyfill
*
* @param arrow
* @text Flèche du spinner
* @type file
* @default arrow
* @dir img/system
* @require 1
*
* @param click
* @text Icone de clic du spinner
* @type file
* @default click
* @dir img/system
* @require 1
*
* @param robot
* @text Icone d'arrêt automatique du spinner
* @type file
* @default robot
* @dir img/system
* @require 1
*/
Scene_Boot.sphinxSpinnerLoadSystemImages = Scene_Boot.loadSystemImages;
Scene_Boot.loadSystemImages = function() {
Scene_Boot.sphinxSpinnerLoadSystemImages.call(this);
ImageManager.reserveSystem(PluginManager.parameters("Sphinx-Spinner")["arrow"]);
ImageManager.reserveSystem(PluginManager.parameters("Sphinx-Spinner")["click"]);
ImageManager.reserveSystem(PluginManager.parameters("Sphinx-Spinner")["robot"]);
}
function SphinxSpinner() {
this.initialize.apply(this, arguments);
};
SphinxSpinner.prototype = Object.create(Sprite_Base.prototype);
SphinxSpinner.prototype.constructor = SphinxSpinner;
SphinxSpinner.getIcon = function(iconIndex) {
var bitmap = ImageManager.loadSystem('IconSet');
var pw = Window_Base._iconWidth;
var ph = Window_Base._iconHeight;
var icon = new Bitmap(pw, ph);
var sx = iconIndex % 16 * pw;
var sy = Math.floor(iconIndex / 16) * ph;
icon.blt(bitmap, sx, sy, pw, ph, 0, 0);
return icon;
};
SphinxSpinner.prototype.initialize = function(config) {
Sprite_Base.prototype.initialize.call(this);
// Configuration
this.config = config || {};
this.auto = config.auto || false;
this.width = config.width || 640;
this.height = config.height || 420;
this.config.values = this.config.values || [ "Pile", "Face" ];
this.config.colors = this.config.colors || [ "red", "blue" ];
this.config.speed = this.config.speed || {};
this.config.speed.start = this.config.speed.start || 1;
this.config.speed.max = this.config.speed.max || 50;
this.config.speed.acceleration = this.config.speed.acceleration || 5;
// Internal variables
this.speed = this.config.speed.start / 100;
this.touched = false;
this.autoStopping = false;
this.stopped = false;
this.frames = 0;
this.anchor.x = 0.5;
this.anchor.y = 0.5;
this.indexes = [];
for(i = 0; i < this.config.values.length; ++i) {
this.indexes.push(i);
}
// Spinner sprite
this.spinner = new Sprite_Button();
this.spinner.anchor.x = 0.5;
this.spinner.anchor.y = 0.5;
this.spinner.bitmap = new Bitmap(this.width, this.height);
this.spinnerContext = this.spinner.bitmap._context;
this.spinnerContext.translate(this.width / 2, this.height / 2);
this.addChild(this.spinner);
// Drawing
this.drawClick();
this.drawArrow();
this.drawSpinner();
// Click listener
if(!this.auto) this.spinner.setClickHandler(SphinxSpinner.prototype.onTouch.bind(this));
};
SphinxSpinner.prototype.update = function() {
Sprite_Base.prototype.update.call(this);
// Rotation
this.spinner.rotation -= this.speed;
// Scale of hand
if(this._click.bitmap) {
if(this.isBusy()) this._click.opacity = 0;
else this._click.opacity = 255;
this._click.scale.x = 1 - 0.5 * this.frames / 60;
this._click.scale.y = 1 - 0.5 * this.frames / 60;
if(this.frames == 60) this.frames = 0;
this.frames++;
}
if(!this.isBusy() && this.auto) {
var rand = Math.randomIntBetween(0, 50);
if(rand == 0) this.autoStopping = true;
}
// Acceleration and deceleration
if(this.speed < this.config.speed.max / 100 && !(this.touched || this.autoStopping)) this.speed *= 1 + this.config.speed.acceleration / 100;
if(this.speed >= this.config.speed.start / 100 && (this.touched || this.autoStopping)) this.speed /= 1 + this.config.speed.acceleration / 100;
else if(this.speed < this.config.speed.start / 100 && !this.stopped) this.stop();
};
SphinxSpinner.prototype.setStopHandler = function(callback) {
if(this.stopped) callback(this.value);
else this.callback = callback;
};
SphinxSpinner.prototype.drawClick = function() {
this._click = new Sprite_Base();
this._click.anchor.x = 0.5;
this._click.anchor.y = 0.5;
this.addChild(this._click);
var clickBitmap = ImageManager.loadSystem(PluginManager.parameters("Sphinx-Spinner")["click"]);
if(this.auto) clickBitmap = ImageManager.loadSystem(PluginManager.parameters("Sphinx-Spinner")["robot"]);
clickBitmap.addLoadListener((() => {
var scale = Math.min(this.width, this.height) * 1 / 3 / clickBitmap.width;
this._click.bitmap = new Bitmap(clickBitmap.width * scale, clickBitmap.height * scale);
this._click.bitmap.blt(clickBitmap, 0, 0, clickBitmap.width, clickBitmap.height, 0, 0, this._click.bitmap.width, this._click.bitmap.height);
}).bind(this));
};
SphinxSpinner.prototype.drawSpinner = function() {
this.indexes.shuffle();
var ray = Math.min(this.width, this.height) / 2 - 8;
var gradient = this.spinnerContext.createRadialGradient(0, 0, 0, 0, 0, ray);
gradient.addColorStop(0, this.config.colors.shift());
for(i = 0; i < this.config.colors.length; ++i) {
gradient.addColorStop((i + 1) * 1 / this.config.colors.length, this.config.colors[i]);
}
for(i = 0; i < this.indexes.length; ++i) {
var v = this.config.values[this.indexes[i]];
this.spinnerContext.beginPath(0, 0);
this.spinnerContext.moveTo(0, 0);
var startAngle = i * 360 / this.indexes.length - 360 / 2 / this.indexes.length;
var nextAngle = i * 360 / this.indexes.length + 360 / 2 / this.indexes.length;
this.spinnerContext.lineTo(ray * Math.cos(startAngle * Math.PI / 180), ray * Math.sin(startAngle * Math.PI / 180));
this.spinnerContext.arc(0, 0, ray, startAngle * Math.PI / 180, nextAngle * Math.PI / 180);
this.spinnerContext.lineWidth = 2;
this.spinnerContext.strokeStyle = "black";
this.spinnerContext.stroke();
this.spinnerContext.save();
this.spinnerContext.globalAlpha = 0.59765625;
this.spinnerContext.fillStyle = gradient;
this.spinnerContext.fill();
this.spinnerContext.restore();
this.spinnerContext.textAlign = "center";
this.spinnerContext.textBaseline = "middle";
this.spinnerContext.save();
this.spinnerContext.rotate(i * 360 / this.indexes.length * Math.PI / 180);
if(v instanceof Bitmap) {
var x1 = ray / 2 * Math.cos(startAngle * Math.PI / 180);
var y1 = ray / 2 * Math.sin(startAngle * Math.PI / 180);
var x2 = ray / 2 * Math.cos(nextAngle * Math.PI / 180);
var y2 = ray / 2 * Math.sin(nextAngle * Math.PI / 180);
var size = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) / 2;
var scale = size / Math.max(v.width, v.height);
this.spinner.bitmap.blt(v, 0, 0, v.width, v.height, ray * 4 / 5 - v.width * scale, -v.height * scale / 2, v.width * scale, v.height * scale);
} else {
var fontSize = ray / (this.config.values.length / 2);
this.spinner.bitmap.fontSize = fontSize;
while(this.spinner.bitmap.measureTextWidth(v.toString()) > ray / 2) {
fontSize--;
this.spinner.bitmap.fontSize = fontSize;
}
this.spinnerContext.font = this.spinner.bitmap._makeFontNameText();
this.spinnerContext.fillText(v.toString(), ray * 3 / 4 - this.spinner.bitmap.measureTextWidth(v.toString()) / 4, 0);
}
this.spinnerContext.restore();
}
};
SphinxSpinner.prototype.drawArrow = function() {
this._arrow = new Sprite_Base();
this._arrow.anchor.x = 0.5;
this._arrow.anchor.y = 0.5;
this._arrow.x = Math.min(this.width, this.height) / 2 - this._arrow.width;
this.addChild(this._arrow);
var arrowBitmap = ImageManager.loadSystem(PluginManager.parameters("Sphinx-Spinner")["arrow"]);
arrowBitmap.addLoadListener((() => {
var scale = Math.min(this.width, this.height) * 1 / 4 / arrowBitmap.width;
this._arrow.bitmap = new Bitmap(arrowBitmap.width * scale, arrowBitmap.height * scale);
this._arrow.bitmap.blt(arrowBitmap, 0, 0, arrowBitmap.width, arrowBitmap.height, 0, 0, this._arrow.bitmap.width, this._arrow.bitmap.height);
}).bind(this));
};
SphinxSpinner.prototype.isBusy = function() {
return this.speed < this.config.speed.max / 100 || this.touched || this.autoStopping;
};
SphinxSpinner.prototype.isStopped = function() {
return this.stopped;
};
SphinxSpinner.prototype.onTouch = function() {
if(this.isBusy()) return;
if(this.auto) return;
this.touched = true;
};
SphinxSpinner.prototype.stop = function() {
this.stopped = true;
this.speed = 0;
var i = Math.ceil((((this.spinner.rotation / Math.PI * 180) - 360 / 2 / this.config.values.length) % 360) / (360 / this.config.values.length)) * -1;
this.value = this.indexes[i];
if(this.callback) this.callback(this.value);
};
Utilisation
Pour utiliser ce script, vous aurez aussi besoin de ces 3 images (ou des images équivalentes) :
Elles sont à placer dans le dossier img/system. La première image est à assigner au paramètre arrow, la seconde au paramètre click et la dernière au paramètre robot.
Ce script n’est pas destiné à être utilisé dans des events. Voici le constructeur complet :
spinner = new SphinxSpinner({
auto: false,
width: 640,
height: 480,
values: [ "Pile", "Face" ],
colors: [ "red", "blue" ],
speed: {
start: 1,
max: 50,
acceleration: 5
}
});
Ce sont les valeurs par défaut. Vous n’êtes pas obligé de définir toutes les clés, les clés non définies seront initialisées à leur valeur par défaut.
Par la suite, vous devrez définir une fonction callback qui sera appelée imédiatement après que le spinner se sera arrêté ainsi :
spinner.setStopHandler(((index) => {
// value est la valeur retournée par le spinner : l'indice correspondant à
// la valeur dans le tableau passé au constructeur.
}).bind(this));
Utilisation en mode manuel
C’est le mode d’exécution par défaut. Dans ce mode, le spinner affiche une icone de clic quand le joueur est autorisé à l’arrêter (autrement dit, quand le spinner a atteint sa vitesse maximale). Lorsque le joueur clique, le spinner ralentit et finit par s’arrêter. La valeur retournée par le spinner est celle que pointe la flèche lorsqu’il finit par s’arrêter.
Utilisation en mode automatique
Dans certaines situations, vous voudrez peut-être faire tourner un spinner, pour générer une valeur aléatoire, sans que ce soit au joueur de choisir quand doit s’arrêter le spinner. Aussi j’ai prévu un mode automatique qui s’active en passant la configuration « auto » à true. Le spinner tournera normalement, et un petit robot s’affichera (à la place de la main indiquant au joueur qu’il peut arrêter le spinner). A chaque frame, le spinner aura une chance sur 50 de s’arrêter. Une fois l’arrêt automatique lancé, le spinner s’arrêtera exactement de la même facon qu’en mode manuel, et la valeur obtenue sera celle pointée par la flèche une fois le spinner totalement arrêté.
Exemple
Comme souvent, j’ai prévu une scène d’exemple. Celle-ci vous demandera le type de spinner que vous voulez créer puis le nombre d’éléments de la roue (entre 2 et 20).
Voici la scène :
//=============================================================================
// Sphinx-SpinnerScene.js
//=============================================================================
/*:
* @plugindesc Scène de démonstration du plugin Sphinx-Chart2
* @author Sphinx
*
* @help
* //==========================================================================
* // Plugin : Sphinx-SpinnerScene
* // Date : 9 janvier 2020
* // Auteur : Sphinx
* //==========================================================================
*
* Dépendances :
* - Sphinx-Spinner
* @param background
* @text Image d'arrière plan de la scène
* @type file
* @dir img/titles1/
* @require 1
*/
SPINNER_TYPES = [ "basique", "texte", "icones", "mixte" ]
CSS_COLORS = [ "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen" ];
//-----------------------------------------------------------------------------
// Game_Interpreter
//
// Commandes de plugin
Sphinx_Chart_Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
Sphinx_Chart_Game_Interpreter_pluginCommand.call(this, command, args);
if(command === "START_SPINNER_SCENE") {
SceneManager.push(Scene_Spinner);
}
};
//-----------------------------------------------------------------------------
// Scene_Spinner
//
// Scène de démonstration du plugin Sphinx-Chart2
function Scene_Spinner() {
this.initialize.apply(this, arguments);
};
Scene_Spinner.prototype = Object.create(Scene_Base.prototype);
Scene_Spinner.prototype.constructor = Scene_Spinner;
Scene_Spinner.prototype.initialize = function() {
Scene_Base.prototype.initialize.call(this);
};
Scene_Spinner.prototype.create = function() {
Scene_Base.prototype.create.call(this);
this.spinners = [];
this.valueSprites = [];
this.intervals = [];
this.createWindowLayer();
this.createBackground();
this.createWindows();
this.createTexts();
};
Scene_Spinner.prototype.start = function() {
Scene_Base.prototype.start.call(this);
this.startFadeIn(this.fadeSpeed(), false);
this._windowTypeSpinner.open();
};
Scene_Spinner.prototype.terminate = function() {
Scene_Base.prototype.terminate.call(this);
SceneManager.goto(Scene_Map);
this.startFadeOut(this.fadeSpeed(), false);
};
Scene_Spinner.prototype.update = function() {
Scene_Base.prototype.update.call(this);
busy = false;
for(spinner of this.spinners) {
if(!spinner.isStopped()) busy = true;
}
for(valueSprite of this.valueSprites) {
if(valueSprite instanceof Sprite_Base && valueSprite.scale.x > 0) busy = true;
}
if(busy) {
if(this._windowTypeSpinner.active) this._windowTypeSpinner.deactivate();
}
else {
if(!this._windowTypeSpinner.active && !this._windowChooseSpinnerType.active) this._windowTypeSpinner.activate();
}
if(Input.isTriggered("cancel")) {
SoundManager.playCancel();
this.terminate();
}
};
Scene_Spinner.prototype.createBackground = function() {
this._background = new Sprite_Base();
background = PluginManager.parameters("Sphinx-SpinnerScene")["background"];
this._background.bitmap = ImageManager.loadTitle1(background);
this.addChildAt(this._background, 0);
};
Scene_Spinner.prototype.createWindows = function() {
this._windowTypeSpinner = new Window_TypeSpinner();
for(type of SPINNER_TYPES) {
this._windowTypeSpinner.setHandler("spin_" + type, this.chooseType.bind(this, type));
}
this.addWindow(this._windowTypeSpinner);
this._windowChooseSpinnerType = new Window_ChooseItemNumber();
for(i = 2; i <= 20; ++i) {
this._windowChooseSpinnerType.setHandler("choose_" + i, this.chooseItemsCount.bind(this, i));
}
this.addWindow(this._windowChooseSpinnerType);
};
Scene_Spinner.prototype.createTexts = function() {
this._titleText = new Sprite_Base();
this._titleText.x = 0;
this._titleText.y = 0;
this._titleText.width = Graphics.boxWidth;
this._titleText.height = 64;
this._titleText.bitmap = new Bitmap(Graphics.boxWidth, 64);
this._titleText.bitmap.fontSize = 28;
this._titleText.bitmap.drawText("Démonstration - Sphinx-Spinner", 0, 16, Graphics.boxWidth - 32, 64, "center");
this.addChildAt(this._titleText, 1);
this._explicationText1 = new Sprite_Base();
this._explicationText1.x = 0;
this._explicationText1.y = Graphics.boxHeight - ((Graphics.boxHeight - 480) / 2);
this._explicationText1.width = Graphics.boxWidth;
this._explicationText1.height = (Graphics.boxHeight - 480) / 4;
this._explicationText1.bitmap = new Bitmap(Graphics.boxWidth, (Graphics.boxHeight - 480) / 4);
this._explicationText1.bitmap.fontSize = 18;
this._explicationText1.bitmap.drawText("Choisissez un type de spinner, puis le nombre d'items.", 0, 8, Graphics.boxWidth - 32, 20, "center");
this.addChildAt(this._explicationText1, 2);
this._explicationText2 = new Sprite_Base();
this._explicationText2.x = 0;
this._explicationText2.y = Graphics.boxHeight - ((Graphics.boxHeight - 480) / 4);
this._explicationText2.width = Graphics.boxWidth;
this._explicationText2.height = (Graphics.boxHeight - 480) / 4;
this._explicationText2.bitmap = new Bitmap(Graphics.boxWidth, (Graphics.boxHeight - 480) / 4);
this._explicationText2.bitmap.fontSize = 18;
this._explicationText2.bitmap.drawText("Pour arrêter le spinner, cliquez dessus.", 0, 8, Graphics.boxWidth - 32, 20, "center");
this.addChildAt(this._explicationText2, 3);
};
Scene_Spinner.prototype.chooseType = function(type) {
this.type = type;
this._windowChooseSpinnerType.open();
this._windowChooseSpinnerType.activate();
}
Scene_Spinner.prototype.chooseItemsCount = function(count) {
this._windowChooseSpinnerType.close();
if(this.spinners) {
for(spinner of this.spinners) {
spinner.destroy();
}
}
this.spinners = [];
if(this.valueSprites) {
for(valueSprite of this.valueSprites) {
if(valueSprite instanceof Sprite_Base) valueSprite.destroy();
}
}
this.valueSprites = [];
this.spinnerValues = [];
this.intervals = [];
spinnerCount = Math.randomIntBetween(1, 1);
for(n = 1; n <= spinnerCount; ++n) {
switch(spinnerCount) {
case 1:
width = 640;
height = 480;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 }
];
break;
case 2:
width = 304;
height = 480;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height / 2 }
];
break;
case 3:
width = 304;
height = 240;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height / 2 },
{ x: 166 + width / 2 + 8 + width / 2, y: 64 + height + height / 2 }
];
break;
case 4:
width = 304;
height = 240;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height / 2 },
{ x: 166 + width / 2, y: 64 + height + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height + height / 2 }
];
break;
case 5:
width = 197.33333;
height = 240;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height / 2 },
{ x: 166 + (width + 16) * 2 + width / 2, y: 64 + height / 2 },
{ x: 166 + width / 2 + width / 2, y: 64 + height + height / 2 },
{ x: 166 + width + 16 + width / 2 + width / 2, y: 64 + height + height / 2 },
];
break;
case 6:
width = 197.33333;
height = 240;
coords = [
{ x: 166 + width / 2, y: 64 + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height / 2 },
{ x: 166 + (width + 16) * 2 + width / 2, y: 64 + height / 2 },
{ x: 166 + width / 2, y: 64 + height + height / 2 },
{ x: 166 + width + 16 + width / 2, y: 64 + height + height / 2 },
{ x: 166 + (width + 16) * 2 + width / 2, y: 64 + height + height / 2 },
];
break;
}
this.spinnerValues[n] = this.getValues(count);
spinner = new SphinxSpinner({
width: width,
height: height,
values: this.spinnerValues[n],
colors: this.getColors(),
});
spinner.x = coords[n - 1].x,
spinner.y = coords[n - 1].y;
spinner.setStopHandler(((n, spinner) => {
this.drawValue(n, spinner);
this.intervals[n] = setInterval(() => {
spinnerHidden = false;
valueDisplayed = false;
spinner.scale.x /= 1.075;
spinner.scale.y /= 1.075;
this.valueSprites[n].scale.x *= 1.2;
this.valueSprites[n].scale.y *= 1.2;
if(this.valueSprites[n].scale.x >= 0.95 && this.valueSprites[n].scale.y >= 0.95) {
valueDisplayed = true;
this.valueSprites[n].scale.x = 1;
this.valueSprites[n].scale.y = 1;
}
if(spinner.scale.x <= 0.01 || spinner.scale.y <= 0.01) {
spinner.scale.x = 0;
spinner.scale.y = 0;
spinnerHidden = true;
}
if(valueDisplayed && spinnerHidden) {
setTimeout(() => {
this.intervals[this.spinners.length + n] = setInterval(() => {
this.valueSprites[n].scale.x /= 1.2;
this.valueSprites[n].scale.y /= 1.2;
if(this.valueSprites[n].scale.x <= 0.01 && this.valueSprites[n].scale.y <= 0.01) {
this.valueSprites[n].scale.x = 0;
this.valueSprites[n].scale.y = 0;
clearInterval(this.intervals[this.spinners.length + n]);
}
}, 1000 / 60);
}, 2500);
clearInterval(this.intervals[n]);
}
}, 1000 / 60);
}).bind(this, n, spinner));
this.spinners.push(spinner);
}
for(i = 0; i < this.spinners.length; ++i) {
this.addChildAt(this.spinners[i], 4 + i);
}
};
Scene_Spinner.prototype.getValues = function(count) {
values = [];
iconIds = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ];
for(c = 1; c <= count; ++c) {
switch(this.type) {
case "basique":
values.push(c);
break;
case "texte":
values.push(CSS_COLORS[Math.randomIntBetween(0, CSS_COLORS.length)]);
break;
case "icones":
i = Math.randomIntBetween(0, iconIds.length);
bitmap = SphinxSpinner.getIcon(iconIds.splice(i, 1));
values.push(bitmap);
break;
case "mixte":
switch(Math.randomIntBetween(1, 4)) {
case 1:
values.push(c);
break;
case 2:
values.push(CSS_COLORS[Math.randomIntBetween(0, CSS_COLORS.length)]);
break;
case 3:
i = Math.randomIntBetween(0, iconIds.length);
bitmap = SphinxSpinner.getIcon(iconIds.splice(i, 1));
values.push(bitmap);
break;
}
break;
}
}
return values;
};
Scene_Spinner.prototype.getColors = function() {
colors = [];
for(c = 1; c <= Math.randomIntBetween(2, 10); ++c) {
do {
color = CSS_COLORS[Math.randomIntBetween(0, CSS_COLORS.length)];
} while(colors.includes(color));
colors.push(color);
}
return colors;
};
Scene_Spinner.prototype.drawValue = function(n, spinner) {
valueSprite = new Sprite_Base();
valueSprite.y = spinner.y;
valueSprite.x = spinner.x;
valueSprite.anchor.x = 0.5;
valueSprite.anchor.y = 0.5;
valueSprite.scale.x = 0.01;
valueSprite.scale.y = 0.01;
if(this.spinnerValues[n][spinner.value] instanceof Bitmap) {
valueSprite.bitmap = new Bitmap(256, 256);
valueSprite.bitmap.blt(this.spinnerValues[n][spinner.value], 0, 0, this.spinnerValues[n][spinner.value].width, this.spinnerValues[n][spinner.value].height, 0, 0, valueSprite.width, valueSprite. height);
} else {
fontSize = 112;
valueSprite.bitmap = new Bitmap(spinner.width, 256);
valueSprite.bitmap.fontSize = fontSize;
while(valueSprite.bitmap.measureTextWidth(this.spinnerValues[n][spinner.value].toString()) > spinner.width) {
fontSize--;
valueSprite.bitmap.fontSize = fontSize;
}
valueSprite.bitmap.drawText(this.spinnerValues[n][spinner.value], 0, 0, spinner.width, 256, "center");
}
this.addChildAt(valueSprite, this.children.length);
this.valueSprites[n] = valueSprite;
};
Scene_Spinner.prototype.fastForwardRate = function() {
return 3;
};
Scene_Spinner.prototype.isFastForward = function() {
return (Input.isPressed('ok') || Input.isPressed('shift') || TouchInput.isPressed());
};
Scene_Spinner.prototype.updateFade = function() {
if(this._fadeDuration > 0) {
var d = this._fadeDuration;
if(this.isFastForward()) {
d -= this.fastForwardRate() - 1;
this._fadeDuration -= this.fastForwardRate();
} else {
this._fadeDuration--;
}
if(this._fadeSign > 0) {
this._fadeSprite.opacity -= this._fadeSprite.opacity / d;
} else {
this._fadeSprite.opacity += (255 - this._fadeSprite.opacity) / d;
}
}
};
Scene_Spinner.prototype.fadeSpeed = function(ignoreFast) {
return 30;
};
//-----------------------------------------------------------------------------
// Window_TypeSpinner
//
// Fenêtre de sélection du type du graphique
function Window_TypeSpinner() {
this.initialize.apply(this, arguments);
};
Window_TypeSpinner.prototype = Object.create(Window_Command.prototype);
Window_TypeSpinner.prototype.constructor = Window_TypeSpinner;
Window_TypeSpinner.prototype.initialize = function() {
Window_Command.prototype.initialize.call(this, 0, 0);
this.updatePlacement();
};
Window_TypeSpinner.prototype.windowWidth = function() {
return 150;
};
Window_TypeSpinner.prototype.updatePlacement = function() {
this.x = 8;
this.y = (Graphics.boxHeight - this.height) / 2;
};
Window_TypeSpinner.prototype.makeCommandList = function() {
for(type of SPINNER_TYPES) {
this.addCommand(type.capitalize(), "spin_" + type);
}
};
Window_TypeSpinner.prototype.isContinueEnabled = function() {
return DataManager.isAnySavefileExists();
};
//-----------------------------------------------------------------------------
// Window_ChooseItemNumber
//
// Fenêtre de sélection du type d'accélération
function Window_ChooseItemNumber() {
this.initialize.apply(this, arguments);
};
Window_ChooseItemNumber.prototype = Object.create(Window_Command.prototype);
Window_ChooseItemNumber.prototype.constructor = Window_ChooseItemNumber;
Window_ChooseItemNumber.prototype.initialize = function() {
Window_Command.prototype.initialize.call(this, 0, 0);
this.close();
this.updatePlacement();
};
Window_ChooseItemNumber.prototype.maxCols = function() {
return 5;
};
Window_ChooseItemNumber.prototype.windowWidth = function() {
return Graphics.boxWidth - 64;
};
Window_ChooseItemNumber.prototype.updatePlacement = function() {
this.x = (Graphics.boxWidth - this.width) / 2;
this.y = (Graphics.boxHeight - this.height) / 2;
};
Window_ChooseItemNumber.prototype.makeCommandList = function() {
for(i = 2; i <= 20; ++i) {
this.addCommand(i.toString(), "choose_" + i);
}
};
Pour l’ouvrir, utilisez la commande de plugin suivante : START_SPINNER_SCENE