Implement sprite-based demon animations, add insufficient funds game over logic, and enhance UI message animations with word-by-word effects
This commit is contained in:
parent
55c45dab36
commit
2fc13c9be4
|
|
@ -104,6 +104,24 @@ export default class GameScene extends Phaser.Scene {
|
||||||
if (!this._gameOver) this.slotMachine.setEnabled(true);
|
if (!this._gameOver) this.slotMachine.setEnabled(true);
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
// Insufficient funds — Sin wins by default; play its victory video and music
|
||||||
|
this.game.events.on('insufficient-funds', () => {
|
||||||
|
if (this._gameOver) return;
|
||||||
|
this._gameOver = true;
|
||||||
|
this._setVideo('sin-victory', false);
|
||||||
|
|
||||||
|
if (this._musicSound) {
|
||||||
|
const outgoing = this._musicSound;
|
||||||
|
this._musicSound = null;
|
||||||
|
this._currentMusicKey = null;
|
||||||
|
this.tweens.add({
|
||||||
|
targets: outgoing, volume: 0, duration: 1000,
|
||||||
|
onComplete: () => { outgoing.stop(); outgoing.destroy(); },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.sound.play('music-sin-victory');
|
||||||
|
}, this);
|
||||||
|
|
||||||
// Victory: play the victory video, music, and shatter the losing vial
|
// Victory: play the victory video, music, and shatter the losing vial
|
||||||
this.game.events.on('vial-winner', ({ winner }) => {
|
this.game.events.on('vial-winner', ({ winner }) => {
|
||||||
this._gameOver = true;
|
this._gameOver = true;
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ const BOTTOM_BAR_Y = 900 - BOTTOM_BAR_HEIGHT;
|
||||||
// Box centers (x) for the three top fund displays
|
// Box centers (x) for the three top fund displays
|
||||||
const BOX_WIDTH = 1600 / 3;
|
const BOX_WIDTH = 1600 / 3;
|
||||||
const PLAYER_BOX_X = BOX_WIDTH * 0 + BOX_WIDTH / 2;
|
const PLAYER_BOX_X = BOX_WIDTH * 0 + BOX_WIDTH / 2;
|
||||||
const LORD_BOX_X = BOX_WIDTH * 1 + BOX_WIDTH / 2;
|
const LORD_BOX_X = BOX_WIDTH * 1 + BOX_WIDTH / 2;
|
||||||
const SIN_BOX_X = BOX_WIDTH * 2 + BOX_WIDTH / 2;
|
const SIN_BOX_X = BOX_WIDTH * 2 + BOX_WIDTH / 2;
|
||||||
const BOX_CENTER_Y = TOP_BAR_HEIGHT / 2;
|
const BOX_CENTER_Y = TOP_BAR_HEIGHT / 2;
|
||||||
|
|
||||||
// Resting positions for the two bottom-bar text objects
|
// Center x for bottom-bar messages; resting y for main line and sub line
|
||||||
const MSG_X = 700;
|
const MSG_X = 700;
|
||||||
const MSG_Y = BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2; // 845
|
const MSG_Y = BOTTOM_BAR_Y + 38; // 828 — main message line
|
||||||
const SUB_Y = BOTTOM_BAR_Y + BOTTOM_BAR_HEIGHT / 2 + 30; // 875
|
const SUB_Y = BOTTOM_BAR_Y + 78; // 868 — sub-message line
|
||||||
|
|
||||||
const pick = arr => arr[Phaser.Math.Between(0, arr.length - 1)];
|
const pick = arr => arr[Phaser.Math.Between(0, arr.length - 1)];
|
||||||
|
|
||||||
|
|
@ -161,7 +161,8 @@ export default class UIScene extends Phaser.Scene {
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this._spinDisabled = false;
|
this._spinDisabled = false;
|
||||||
this._gameOver = false;
|
this._gameOver = false;
|
||||||
|
this._wordObjects = [];
|
||||||
this._buildTopBar();
|
this._buildTopBar();
|
||||||
this._buildBottomBar();
|
this._buildBottomBar();
|
||||||
this._buildSpinButton();
|
this._buildSpinButton();
|
||||||
|
|
@ -197,8 +198,8 @@ export default class UIScene extends Phaser.Scene {
|
||||||
alpha: 0.8
|
alpha: 0.8
|
||||||
};
|
};
|
||||||
this.add.text(PLAYER_BOX_X, 14, 'YOUR FUNDS', labelStyle).setOrigin(0.5, 0);
|
this.add.text(PLAYER_BOX_X, 14, 'YOUR FUNDS', labelStyle).setOrigin(0.5, 0);
|
||||||
this.add.text(LORD_BOX_X, 14, 'THE LORD', labelStyle).setOrigin(0.5, 0);
|
this.add.text(LORD_BOX_X, 14, 'THE LORD', labelStyle).setOrigin(0.5, 0);
|
||||||
this.add.text(SIN_BOX_X, 14, 'SIN', labelStyle).setOrigin(0.5, 0);
|
this.add.text(SIN_BOX_X, 14, 'SIN', labelStyle).setOrigin(0.5, 0);
|
||||||
|
|
||||||
// Fund value texts
|
// Fund value texts
|
||||||
const valueStyle = {
|
const valueStyle = {
|
||||||
|
|
@ -209,8 +210,8 @@ export default class UIScene extends Phaser.Scene {
|
||||||
strokeThickness: 3
|
strokeThickness: 3
|
||||||
};
|
};
|
||||||
this.playerText = this.add.text(PLAYER_BOX_X, 55, '$1000', valueStyle).setOrigin(0.5, 0.5);
|
this.playerText = this.add.text(PLAYER_BOX_X, 55, '$1000', valueStyle).setOrigin(0.5, 0.5);
|
||||||
this.lordText = this.add.text(LORD_BOX_X, 55, '$0', valueStyle).setOrigin(0.5, 0.5);
|
this.lordText = this.add.text(LORD_BOX_X, 55, '$0', valueStyle).setOrigin(0.5, 0.5);
|
||||||
this.sinText = this.add.text(SIN_BOX_X, 55, '$0', { ...valueStyle, color: '#ff4444' }).setOrigin(0.5, 0.5);
|
this.sinText = this.add.text(SIN_BOX_X, 55, '$0', { ...valueStyle, color: '#ff4444' }).setOrigin(0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildBottomBar() {
|
_buildBottomBar() {
|
||||||
|
|
@ -221,21 +222,22 @@ export default class UIScene extends Phaser.Scene {
|
||||||
g.lineStyle(2, 0xffd700, 0.8);
|
g.lineStyle(2, 0xffd700, 0.8);
|
||||||
g.strokeRect(0, BOTTOM_BAR_Y, 1600, BOTTOM_BAR_HEIGHT);
|
g.strokeRect(0, BOTTOM_BAR_Y, 1600, BOTTOM_BAR_HEIGHT);
|
||||||
|
|
||||||
this.messageText = this.add.text(MSG_X, MSG_Y, 'Press SPIN or SPACE to begin', {
|
// Static text used for initial state, game-over, and insufficient-funds messages
|
||||||
fontSize: '22px',
|
this.messageText = this.add.text(MSG_X, MSG_Y, 'Press SPIN or SPACE to test your Righteousness against Temptation', {
|
||||||
|
fontSize: '26px',
|
||||||
fontFamily: 'Georgia, serif',
|
fontFamily: 'Georgia, serif',
|
||||||
color: '#e8d8b0',
|
color: '#e8d8b0',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
wordWrap: { width: 1100 }
|
wordWrap: { width: 1100 }
|
||||||
}).setOrigin(0.5, 0.5);
|
}).setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
// Secondary sub-message (hidden by default)
|
// Sub-message static text (hidden by default; used for special messages)
|
||||||
this.redeemText = this.add.text(MSG_X, SUB_Y, '', {
|
this.redeemText = this.add.text(MSG_X, SUB_Y, '$50 per spin. Tithe 40% of winnings to THE LORD. Losses go towards your SIN. Win if THE LORD reaches $2000. Lose if SIN reaches $2000 or you run out of money.', {
|
||||||
fontSize: '16px',
|
fontSize: '18px',
|
||||||
fontFamily: 'Georgia, serif',
|
fontFamily: 'Georgia, serif',
|
||||||
color: '#ff9944',
|
color: '#ff9944',
|
||||||
align: 'center'
|
align: 'center'
|
||||||
}).setOrigin(0.5, 0.5).setAlpha(0);
|
}).setOrigin(0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildSpinButton() {
|
_buildSpinButton() {
|
||||||
|
|
@ -262,7 +264,7 @@ export default class UIScene extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.spinBtnHitArea.on('pointerover', () => { if (!this._spinDisabled) this._drawSpinBtn(true); });
|
this.spinBtnHitArea.on('pointerover', () => { if (!this._spinDisabled) this._drawSpinBtn(true); });
|
||||||
this.spinBtnHitArea.on('pointerout', () => { if (!this._spinDisabled) this._drawSpinBtn(false); });
|
this.spinBtnHitArea.on('pointerout', () => { if (!this._spinDisabled) this._drawSpinBtn(false); });
|
||||||
|
|
||||||
this._btnX = btnX;
|
this._btnX = btnX;
|
||||||
this._btnY = btnY;
|
this._btnY = btnY;
|
||||||
|
|
@ -343,8 +345,75 @@ export default class UIScene extends Phaser.Scene {
|
||||||
|
|
||||||
// ── Animated message display ──────────────────────────────────────────────
|
// ── Animated message display ──────────────────────────────────────────────
|
||||||
|
|
||||||
/** Restore both text objects to their neutral resting state. */
|
/** Kill and destroy every per-word text object from the last animation. */
|
||||||
|
_clearWordObjects() {
|
||||||
|
this._wordObjects.forEach(t => {
|
||||||
|
this.tweens.killTweensOf(t);
|
||||||
|
if (t.active) t.destroy();
|
||||||
|
});
|
||||||
|
this._wordObjects = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a row of word texts that individually animate in from a large starting size.
|
||||||
|
*
|
||||||
|
* @param {string[]} words - Words to render.
|
||||||
|
* @param {number} centerX - Horizontal center of the row.
|
||||||
|
* @param {number} finalY - Vertical center of the row at rest.
|
||||||
|
* @param {number} fontSize - Final font size in px.
|
||||||
|
* @param {string} color - CSS colour string.
|
||||||
|
* @param {object} styleExtras - Extra Phaser text style properties.
|
||||||
|
* @param {number} fromScale - Starting scale (>1 = starts huge, <1 = starts tiny).
|
||||||
|
* @param {number} fromY - Y offset from finalY at start (negative = above).
|
||||||
|
* @param {number} msPerWord - Delay increment (ms) between successive words.
|
||||||
|
* @param {number} baseDelay - Global delay (ms) before the first word fires.
|
||||||
|
* @returns {Phaser.GameObjects.Text[]}
|
||||||
|
*/
|
||||||
|
_spawnRowOfWords(words, centerX, finalY, fontSize, color, styleExtras, fromScale, fromY, msPerWord, baseDelay) {
|
||||||
|
const GAP = Math.round(fontSize * 0.38);
|
||||||
|
|
||||||
|
// Create off-screen first so .width is readable for layout.
|
||||||
|
// setOrigin(0.5, 0.5) so that setPosition targets the word's centre,
|
||||||
|
// matching the centre-based layout math below and ensuring the scale
|
||||||
|
// animation shrinks/grows from the middle of each word.
|
||||||
|
const objs = words.map(w =>
|
||||||
|
this.add.text(-4000, -4000, w, {
|
||||||
|
fontSize: `${fontSize}px`,
|
||||||
|
fontFamily: 'Georgia, serif',
|
||||||
|
...styleExtras,
|
||||||
|
}).setOrigin(0.5, 0.5)
|
||||||
|
);
|
||||||
|
|
||||||
|
const totalW = objs.reduce((s, t) => s + t.width, 0) + GAP * Math.max(0, words.length - 1);
|
||||||
|
let x = centerX - totalW / 2;
|
||||||
|
|
||||||
|
objs.forEach((t, i) => {
|
||||||
|
const fx = x + t.width / 2;
|
||||||
|
x += t.width + GAP;
|
||||||
|
|
||||||
|
t.setColor(color)
|
||||||
|
.setPosition(fx, finalY + fromY)
|
||||||
|
.setScale(fromScale)
|
||||||
|
.setAlpha(0);
|
||||||
|
|
||||||
|
this.tweens.add({
|
||||||
|
targets: t,
|
||||||
|
scale: 1,
|
||||||
|
alpha: 1,
|
||||||
|
y: finalY,
|
||||||
|
duration: 280,
|
||||||
|
delay: baseDelay + msPerWord * i,
|
||||||
|
ease: 'Back.easeOut',
|
||||||
|
easeParams: [2.5],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Restore the bottom bar to a clean neutral state (clears word objects). */
|
||||||
_resetMessageTexts() {
|
_resetMessageTexts() {
|
||||||
|
this._clearWordObjects();
|
||||||
this.tweens.killTweensOf(this.messageText);
|
this.tweens.killTweensOf(this.messageText);
|
||||||
this.tweens.killTweensOf(this.redeemText);
|
this.tweens.killTweensOf(this.redeemText);
|
||||||
this.messageText.setPosition(MSG_X, MSG_Y).setAlpha(1).setScale(1);
|
this.messageText.setPosition(MSG_X, MSG_Y).setAlpha(1).setScale(1);
|
||||||
|
|
@ -352,80 +421,77 @@ export default class UIScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Infernal Slam — main text crashes in from the left, sub-text rises from hell.
|
* Infernal Slam — each word of the main line crashes down from above;
|
||||||
|
* sub-line words rise up from below, then pulse.
|
||||||
*/
|
*/
|
||||||
_showLossMessage(main, sub) {
|
_showLossMessage(main, sub) {
|
||||||
|
this._clearWordObjects();
|
||||||
this.tweens.killTweensOf(this.messageText);
|
this.tweens.killTweensOf(this.messageText);
|
||||||
this.tweens.killTweensOf(this.redeemText);
|
this.tweens.killTweensOf(this.redeemText);
|
||||||
|
this.messageText.setText('').setAlpha(0);
|
||||||
|
this.redeemText.setAlpha(0);
|
||||||
|
|
||||||
// Dark red flash across the bar
|
// Dark red flash across the bar
|
||||||
const flash = this.add.rectangle(800, MSG_Y, 1600, BOTTOM_BAR_HEIGHT, 0x990000)
|
const flash = this.add.rectangle(800, MSG_Y, 1600, BOTTOM_BAR_HEIGHT, 0x990000).setAlpha(0.55);
|
||||||
.setAlpha(0.5);
|
this.tweens.add({ targets: flash, alpha: 0, duration: 350, onComplete: () => flash.destroy() });
|
||||||
this.tweens.add({
|
|
||||||
targets: flash, alpha: 0, duration: 350,
|
|
||||||
onComplete: () => flash.destroy(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Main text — slam in from the left with overshoot
|
const mainWords = main.split(/\s+/).filter(Boolean);
|
||||||
this.messageText
|
const subWords = sub.split(/\s+/).filter(Boolean);
|
||||||
.setText(main)
|
|
||||||
.setColor('#ff3333')
|
|
||||||
.setPosition(-320, MSG_Y)
|
|
||||||
.setAlpha(0)
|
|
||||||
.setScale(1.15);
|
|
||||||
|
|
||||||
this.tweens.add({
|
const msMain = 82;
|
||||||
targets: this.messageText,
|
const msSub = 75;
|
||||||
x: MSG_X, alpha: 1, scale: 1,
|
const subStart = mainWords.length * msMain + 180;
|
||||||
duration: 480, ease: 'Back.easeOut', easeParams: [4],
|
|
||||||
onComplete: () => {
|
// Main: each word crashes down from above, starting at 4.5× scale
|
||||||
// Sharp horizontal tremble on arrival
|
const mainObjs = this._spawnRowOfWords(
|
||||||
|
mainWords, MSG_X, MSG_Y,
|
||||||
|
34, '#ff3333', {},
|
||||||
|
4.5, -42,
|
||||||
|
msMain, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sub: each word rises from below, starting at 3× scale
|
||||||
|
const subObjs = this._spawnRowOfWords(
|
||||||
|
subWords, MSG_X, SUB_Y,
|
||||||
|
22, '#ff9944', {},
|
||||||
|
3.0, 28,
|
||||||
|
msSub, subStart
|
||||||
|
);
|
||||||
|
|
||||||
|
this._wordObjects = [...mainObjs, ...subObjs];
|
||||||
|
|
||||||
|
// After sub finishes, pulse all sub words indefinitely
|
||||||
|
const pulseAt = subStart + subWords.length * msSub + 300;
|
||||||
|
this.time.delayedCall(pulseAt, () => {
|
||||||
|
subObjs.forEach(t => {
|
||||||
|
if (!t.active) return;
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: this.messageText,
|
targets: t,
|
||||||
x: { from: MSG_X - 6, to: MSG_X + 6 },
|
|
||||||
duration: 40, yoyo: true, repeat: 5, ease: 'Sine.easeInOut',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sub-text — rises up from below the canvas
|
|
||||||
this.redeemText
|
|
||||||
.setText(sub)
|
|
||||||
.setColor('#ff9944')
|
|
||||||
.setPosition(MSG_X, 920)
|
|
||||||
.setAlpha(0)
|
|
||||||
.setScale(1);
|
|
||||||
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.redeemText,
|
|
||||||
y: SUB_Y, alpha: 1,
|
|
||||||
duration: 420, delay: 380, ease: 'Cubic.easeOut',
|
|
||||||
onComplete: () => {
|
|
||||||
this.tweens.add({
|
|
||||||
targets: this.redeemText,
|
|
||||||
alpha: { from: 1, to: 0.3 },
|
alpha: { from: 1, to: 0.3 },
|
||||||
duration: 850, yoyo: true, repeat: -1,
|
duration: 850,
|
||||||
|
yoyo: true,
|
||||||
|
repeat: -1,
|
||||||
});
|
});
|
||||||
},
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divine Descent — main text drops from the heavens, sub-text blooms into being.
|
* Divine Descent — each word of the main line drops from above in gold;
|
||||||
|
* sub-line words bloom up from a tiny point.
|
||||||
*/
|
*/
|
||||||
_showWinMessage(main, sub) {
|
_showWinMessage(main, sub) {
|
||||||
|
this._clearWordObjects();
|
||||||
this.tweens.killTweensOf(this.messageText);
|
this.tweens.killTweensOf(this.messageText);
|
||||||
this.tweens.killTweensOf(this.redeemText);
|
this.tweens.killTweensOf(this.redeemText);
|
||||||
|
this.messageText.setText('').setAlpha(0);
|
||||||
|
this.redeemText.setAlpha(0);
|
||||||
|
|
||||||
// Golden flash across the bar
|
// Golden flash across the bar
|
||||||
const flash = this.add.rectangle(800, MSG_Y, 1600, BOTTOM_BAR_HEIGHT, 0xffd700)
|
const flash = this.add.rectangle(800, MSG_Y, 1600, BOTTOM_BAR_HEIGHT, 0xffd700).setAlpha(0.4);
|
||||||
.setAlpha(0.4);
|
this.tweens.add({ targets: flash, alpha: 0, duration: 500, onComplete: () => flash.destroy() });
|
||||||
this.tweens.add({
|
|
||||||
targets: flash, alpha: 0, duration: 500,
|
|
||||||
onComplete: () => flash.destroy(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Floating ✦ sparkles scatter across the bar
|
// Floating sparkles scatter across the bar
|
||||||
for (let i = 0; i < 7; i++) {
|
for (let i = 0; i < 7; i++) {
|
||||||
const sp = this.add.text(
|
const sp = this.add.text(
|
||||||
Phaser.Math.Between(100, 1300), MSG_Y + Phaser.Math.Between(-20, 20),
|
Phaser.Math.Between(100, 1300), MSG_Y + Phaser.Math.Between(-20, 20),
|
||||||
|
|
@ -444,33 +510,31 @@ export default class UIScene extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main text — descends from just above the bar, scales down to normal
|
const mainWords = main.split(/\s+/).filter(Boolean);
|
||||||
this.messageText
|
const subWords = sub.split(/\s+/).filter(Boolean);
|
||||||
.setText(main)
|
|
||||||
.setColor('#ffd700')
|
|
||||||
.setPosition(MSG_X, BOTTOM_BAR_Y - 28)
|
|
||||||
.setAlpha(0)
|
|
||||||
.setScale(1.35);
|
|
||||||
|
|
||||||
this.tweens.add({
|
const msMain = 100;
|
||||||
targets: this.messageText,
|
const msSub = 88;
|
||||||
y: MSG_Y, alpha: 1, scale: 1,
|
const subStart = mainWords.length * msMain + 200;
|
||||||
duration: 460, ease: 'Back.easeOut', easeParams: [2.5],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sub-text — blooms up from a tiny point at its resting position
|
// Main: each word descends from above, starting at 3.8× scale, with golden stroke
|
||||||
this.redeemText
|
const mainObjs = this._spawnRowOfWords(
|
||||||
.setText(sub)
|
mainWords, MSG_X, MSG_Y,
|
||||||
.setColor('#c8a87e')
|
34, '#ffd700',
|
||||||
.setPosition(MSG_X, SUB_Y)
|
{ stroke: '#5a3000', strokeThickness: 2 },
|
||||||
.setAlpha(0)
|
3.8, -50,
|
||||||
.setScale(0.15);
|
msMain, 0
|
||||||
|
);
|
||||||
|
|
||||||
this.tweens.add({
|
// Sub: each word blooms up from a tiny point (fromScale 0.2 → 1)
|
||||||
targets: this.redeemText,
|
const subObjs = this._spawnRowOfWords(
|
||||||
scale: 1, alpha: 1,
|
subWords, MSG_X, SUB_Y,
|
||||||
duration: 380, delay: 280, ease: 'Back.easeOut', easeParams: [3],
|
22, '#c8a87e', {},
|
||||||
});
|
0.2, 0,
|
||||||
|
msSub, subStart
|
||||||
|
);
|
||||||
|
|
||||||
|
this._wordObjects = [...mainObjs, ...subObjs];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -494,6 +558,6 @@ export default class UIScene extends Phaser.Scene {
|
||||||
|
|
||||||
// Called by GameScene to position animations toward the right box
|
// Called by GameScene to position animations toward the right box
|
||||||
getPlayerBoxCenter() { return { x: PLAYER_BOX_X, y: BOX_CENTER_Y }; }
|
getPlayerBoxCenter() { return { x: PLAYER_BOX_X, y: BOX_CENTER_Y }; }
|
||||||
getLordBoxCenter() { return { x: LORD_BOX_X, y: BOX_CENTER_Y }; }
|
getLordBoxCenter() { return { x: LORD_BOX_X, y: BOX_CENTER_Y }; }
|
||||||
getSinBoxCenter() { return { x: SIN_BOX_X, y: BOX_CENTER_Y }; }
|
getSinBoxCenter() { return { x: SIN_BOX_X, y: BOX_CENTER_Y }; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue