Browse Source

floodFill

master
jiannibang 6 years ago
parent
commit
d4647f5dca
  1. 10
      Game.vue
  2. 22
      games/game-flood-fill/game.js
  3. 12
      games/game-flood-fill/scenes/BootScene.js
  4. 342
      games/game-flood-fill/scenes/PlayScene.js
  5. BIN
      games/game-flood-fill/scenes/bg.jpg
  6. BIN
      games/game-flood-fill/scenes/bu.png
  7. BIN
      games/game-flood-fill/scenes/gr.png
  8. BIN
      games/game-flood-fill/scenes/grey.png
  9. BIN
      games/game-flood-fill/scenes/instruction.png
  10. BIN
      games/game-flood-fill/scenes/pr.png
  11. BIN
      games/game-flood-fill/scenes/red.png
  12. 146
      games/game-flood-fill/scenes/tiles.json
  13. BIN
      games/game-flood-fill/scenes/tiles.png
  14. BIN
      games/game-flood-fill/scenes/title.png
  15. BIN
      games/game-flood-fill/scenes/ye.png

10
Game.vue

@ -6,10 +6,11 @@ import ballMove from "./games/game-ballmove/game";
import flappyBird from "./games/game-flipbird/game"; import flappyBird from "./games/game-flipbird/game";
import game2048 from "./games/game2048/game"; import game2048 from "./games/game2048/game";
import rect from "./games/game-rect/game"; import rect from "./games/game-rect/game";
const games = { ballMove, flappyBird, game2048, rect };
import floodFill from "./games/game-flood-fill/game";
const games = { ballMove, flappyBird, game2048, rect, floodFill };
const STATE_RUNNING = 0; const STATE_RUNNING = 0;
const STATE_LOSE = 1; const STATE_LOSE = 1;
const STATE_WIN = 2;
const STATE_WON = 2;
const STATE_PAUSED = 3; const STATE_PAUSED = 3;
export default { export default {
props: ["name", "containerStyle"], props: ["name", "containerStyle"],
@ -68,11 +69,14 @@ export default {
if (this.state !== STATE_LOSE) this.$emit("lost"); if (this.state !== STATE_LOSE) this.$emit("lost");
this.state = STATE_LOSE; this.state = STATE_LOSE;
}, },
onWon: () => {
if (this.state !== STATE_WON) this.$emit("won");
this.state = STATE_WON;
},
}); });
}, },
}, },
async mounted() { async mounted() {
console.log("a");
this.reload(this.name); this.reload(this.name);
}, },

22
games/game-flood-fill/game.js

@ -1,13 +1,12 @@
import Phaser from "phaser"; import Phaser from "phaser";
import BootScene from "./scenes/BootScene";
import PlayScene from "./scenes/PlayScene"; import PlayScene from "./scenes/PlayScene";
//import { * } from "./constant"; //import { * } from "./constant";
function launch(containerId) {
function launch({ containerId, onLose, onWon }) {
const container = document.getElementById(containerId); const container = document.getElementById(containerId);
const width = container.clientWidth; const width = container.clientWidth;
const height = container.clientHeight; const height = container.clientHeight;
return new Phaser.Game({
const game = new Phaser.Game({
type: Phaser.WEBGL, type: Phaser.WEBGL,
width, width,
height, height,
@ -21,8 +20,23 @@ function launch(containerId) {
debug: true, debug: true,
}, },
}, },
scene: [BootScene, PlayScene],
scene: PlayScene,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: containerId,
width: 1080,
height: 1080,
},
});
Object.assign(game, {
onLose,
onWon,
restart() {
this.scene.scenes[0].scene.start("PlayScene");
},
}); });
return game;
} }
export default launch; export default launch;

12
games/game-flood-fill/scenes/BootScene.js

@ -1,12 +0,0 @@
import { Scene } from "phaser";
export default class BootScene extends Scene {
constructor() {
super({ key: "BootScene" });
}
preload() {}
create() {
this.scene.start("PlayScene");
}
}

342
games/game-flood-fill/scenes/PlayScene.js

@ -1,9 +1,10 @@
import Phaser from "phaser"; import Phaser from "phaser";
import bg from "@/game/game-breakout/assets/parallax-mountain800600.png";
import atari from "@/game/game-flood-fill/assets/flood/atari-smooth.png";
//import atarixml from "@/game/game-flood-fill/assets/flood/atari-smooth.xml";
import blobs from "@/game/game-flood-fill/assets/flood/blobs.png";
//import blobsjson from "@/game/game-flood-fill/assets/flood/blobs.json";
import bg from "./bg.jpg";
import tiles from "./tiles.png";
import sprites from "./tiles.json";
import title from "./title.png";
import instruction from "./instruction.png";
class PlayScene extends Phaser.Scene { class PlayScene extends Phaser.Scene {
constructor() { constructor() {
@ -12,18 +13,6 @@ class PlayScene extends Phaser.Scene {
}); });
this.allowClick = true; this.allowClick = true;
this.arrow;
this.cursor;
this.cursorTween;
this.monsterTween;
this.icon1 = { shadow: null, monster: null };
this.icon2 = { shadow: null, monster: null };
this.icon3 = { shadow: null, monster: null };
this.icon4 = { shadow: null, monster: null };
this.icon5 = { shadow: null, monster: null };
this.icon6 = { shadow: null, monster: null };
this.gridBG; this.gridBG;
this.instructions; this.instructions;
@ -40,33 +29,34 @@ class PlayScene extends Phaser.Scene {
this.moves = 25; this.moves = 25;
this.frames = ["blue", "green", "grey", "purple", "red", "yellow"];
this.frames = [
"red.png",
"bu.png",
"grey.png",
"ye.png",
"pr.png",
"gr.png",
];
} }
preload() { preload() {
this.load.bitmapFont(
"atari",
atari,
"static/assets/flood-fill/atari-smooth.xml"
);
this.load.atlas("flood", blobs, "static/assets/flood-fill/blobs.json");
this.load.image("bg", bg);
this.load.image("title", title);
this.load.atlas("tiles", tiles, sprites);
this.load.image("instruction", instruction);
} }
create() { create() {
this.add.image(400, 300, "flood", "background");
this.gridBG = this.add.image(400, 600 + 300, "flood", "grid");
this.createIcon(this.icon1, "grey", 16, 156);
this.createIcon(this.icon2, "red", 16, 312);
this.createIcon(this.icon3, "green", 16, 458);
this.createIcon(this.icon4, "yellow", 688, 156);
this.createIcon(this.icon5, "blue", 688, 312);
this.createIcon(this.icon6, "purple", 688, 458);
this.cursor = this.add
.image(16, 156, "flood", "cursor-over")
.setOrigin(0)
.setVisible(false);
const bg = this.add.image(0, 0, "bg");
bg.setOrigin(0);
bg.displayWidth = 1080;
bg.displayHeight = 1080;
this.createIcon(0, 0, 272);
this.createIcon(1, 0, 272 + 252);
this.createIcon(2, 0, 272 + 252 * 2);
this.createIcon(3, 1080 - 184, 272);
this.createIcon(4, 1080 - 184, 272 + 252);
this.createIcon(5, 1080 - 184, 272 + 252 * 2);
// The game is played in a 14x14 grid with 6 different colors // The game is played in a 14x14 grid with 6 different colors
@ -76,12 +66,14 @@ class PlayScene extends Phaser.Scene {
this.grid[x] = []; this.grid[x] = [];
for (var y = 0; y < 14; y++) { for (var y = 0; y < 14; y++) {
var sx = 166 + x * 36;
var sy = 66 + y * 36;
var sx = 204 + x * 48;
var sy = 309 + y * 48;
var color = Phaser.Math.Between(0, 5); var color = Phaser.Math.Between(0, 5);
var block = this.add.image(sx, -600 + sy, "flood", this.frames[color]);
var block = this.add.image(sx, -1032 + sy, "tiles", this.frames[color]);
block.setOrigin(0);
block.displayWidth = 48;
block.displayHeight = 48;
block.setData("oldColor", color); block.setData("oldColor", color);
block.setData("color", color); block.setData("color", color);
block.setData("x", sx); block.setData("x", sx);
@ -102,23 +94,28 @@ class PlayScene extends Phaser.Scene {
this.currentColor = this.grid[0][0].getData("color"); this.currentColor = this.grid[0][0].getData("color");
this.particles = this.add.particles("flood");
this.particles = this.add.particles("tiles");
for (var i = 0; i < this.frames.length; i++) { for (var i = 0; i < this.frames.length; i++) {
this.createEmitter(this.frames[i]); this.createEmitter(this.frames[i]);
} }
this.createArrow();
this.text1 = this.add.bitmapText(684, 30, "atari", "Moves", 20).setAlpha(0);
this.text2 = this.add.bitmapText(694, 60, "atari", "00", 40).setAlpha(0);
const title = this.add.image(1080 / 2, 228, "title");
title.displayWidth = 222;
title.displayHeight = 50;
this.text1 = this.add
.text(440, 160, "MOVES:", {
fontSize: 40,
color: "#fff",
})
.setAlpha(0);
this.text2 = this.add
.text(590, 160, "00", { fontSize: 40, color: "#fff" })
.setAlpha(0);
this.text3 = this.add this.text3 = this.add
.bitmapText(180, 200, "atari", "So close!\n\nClick to\ntry again", 48)
.text(180, 200, "差一点!\n\n点击\n再试一次", 48)
.setAlpha(0); .setAlpha(0);
this.instructions = this.add
.image(400, 300, "flood", "instructions")
.setAlpha(0);
this.instructions = this.add.image(500, 600, "instruction").setAlpha(0);
//初始化位置 //初始化位置
this.revealGrid(); this.revealGrid();
} }
@ -141,40 +138,11 @@ class PlayScene extends Phaser.Scene {
} }
} }
createArrow() {
this.arrow = this.add
.image(109 - 24, 48, "flood", "arrow-white")
.setOrigin(0)
.setAlpha(0);
this.tweens.add({
targets: this.arrow,
x: "+=24",
ease: "Sine.easeInOut",
duration: 900,
yoyo: true,
repeat: -1,
});
}
createIcon(icon, color, x, y) {
var sx = x < 400 ? -200 : 1000;
icon.monster = this.add.image(sx, y, "flood", "icon-" + color).setOrigin(0);
var shadow = this.add.image(sx, y, "flood", "shadow");
shadow.setData("color", this.frames.indexOf(color));
shadow.setData("x", x);
shadow.setData("monster", icon.monster);
shadow.setOrigin(0);
shadow.setInteractive();
icon.shadow = shadow;
createIcon(color, x, y) {
const area = this.add.rectangle(x, y, 184, 758 / 3);
area.setOrigin(0);
area.setData("color", color);
area.setInteractive();
} }
revealGrid() { revealGrid() {
@ -206,53 +174,6 @@ class PlayScene extends Phaser.Scene {
i -= 1000; i -= 1000;
// Icons
this.tweens.add({
targets: [this.icon1.shadow, this.icon1.monster],
x: this.icon1.shadow.getData("x"),
ease: "Power3",
delay: i,
});
this.tweens.add({
targets: [this.icon4.shadow, this.icon4.monster],
x: this.icon4.shadow.getData("x"),
ease: "Power3",
delay: i,
});
i += 200;
this.tweens.add({
targets: [this.icon2.shadow, this.icon2.monster],
x: this.icon2.shadow.getData("x"),
ease: "Power3",
delay: i,
});
this.tweens.add({
targets: [this.icon5.shadow, this.icon5.monster],
x: this.icon5.shadow.getData("x"),
ease: "Power3",
delay: i,
});
i += 200;
this.tweens.add({
targets: [this.icon3.shadow, this.icon3.monster],
x: this.icon3.shadow.getData("x"),
ease: "Power3",
delay: i,
});
this.tweens.add({
targets: [this.icon6.shadow, this.icon6.monster],
x: this.icon6.shadow.getData("x"),
ease: "Power3",
delay: i,
});
// Text // Text
this.tweens.add({ this.tweens.add({
@ -268,7 +189,7 @@ class PlayScene extends Phaser.Scene {
from: 0, from: 0,
to: 25, to: 25,
ease: "Power1", ease: "Power1",
onUpdate: function(tween, targets, text) {
onUpdate: function (tween, targets, text) {
text.setText( text.setText(
Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1) Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1)
); );
@ -280,7 +201,7 @@ class PlayScene extends Phaser.Scene {
i += 500; i += 500;
this.tweens.add({ this.tweens.add({
targets: [this.instructions, this.arrow],
targets: [this.instructions],
alpha: 1, alpha: 1,
ease: "Power3", ease: "Power3",
delay: i, delay: i,
@ -290,15 +211,13 @@ class PlayScene extends Phaser.Scene {
} }
startInputEvents() { startInputEvents() {
this.input.on("gameobjectover", this.onIconOver, this);
this.input.on("gameobjectout", this.onIconOut, this);
this.input.on("gameobjectdown", this.onIconDown, this); this.input.on("gameobjectdown", this.onIconDown, this);
// Cheat mode :) // Cheat mode :)
this.input.keyboard.on( this.input.keyboard.on(
"keydown_M", "keydown_M",
function() {
function () {
this.moves++; this.moves++;
this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1)); this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1));
}, },
@ -307,7 +226,7 @@ class PlayScene extends Phaser.Scene {
this.input.keyboard.on( this.input.keyboard.on(
"keydown_X", "keydown_X",
function() {
function () {
this.moves--; this.moves--;
this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1)); this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1));
}, },
@ -316,72 +235,13 @@ class PlayScene extends Phaser.Scene {
} }
stopInputEvents() { stopInputEvents() {
this.input.off("gameobjectover", this.onIconOver);
this.input.off("gameobjectout", this.onIconOut);
this.input.off("gameobjectdown", this.onIconDown); this.input.off("gameobjectdown", this.onIconDown);
} }
onIconOver(pointer, gameObject) {
var icon = gameObject;
var newColor = icon.getData("color");
// Valid color?
if (newColor !== this.currentColor) {
this.cursor.setFrame("cursor-over");
} else {
this.cursor.setFrame("cursor-invalid");
}
this.cursor.setPosition(icon.x + icon.width / 2, icon.y + icon.height / 2);
if (this.cursorTween) {
this.cursorTween.stop();
}
this.cursor.setAlpha(1);
this.cursor.setVisible(true);
// Change arrow color
this.arrow.setFrame("arrow-" + this.frames[newColor]);
// Jiggle the monster :)
var monster = icon.getData("monster");
this.children.bringToTop(monster);
this.monsterTween = this.tweens.add({
targets: monster,
y: "-=24",
yoyo: true,
repeat: -1,
duration: 300,
ease: "Power2",
});
}
onIconOut(pointer, gameObject) {
// console.log(this.monsterTween.targets[0].y);
this.monsterTween.stop(0);
gameObject.getData("monster").setY(gameObject.y);
// console.log(this.monsterTween.targets[0].y);
this.cursorTween = this.tweens.add({
targets: this.cursor,
alpha: 0,
duration: 300,
});
this.arrow.setFrame("arrow-white");
}
onIconDown(pointer, gameObject) { onIconDown(pointer, gameObject) {
if (!this.allowClick) { if (!this.allowClick) {
return; return;
} }
var icon = gameObject; var icon = gameObject;
var newColor = icon.getData("color"); var newColor = icon.getData("color");
@ -392,7 +252,6 @@ class PlayScene extends Phaser.Scene {
} }
var oldColor = this.grid[0][0].getData("color"); var oldColor = this.grid[0][0].getData("color");
// console.log('starting flood from', oldColor, this.frames[oldColor], 'to', newColor, this.frames[newColor]); // console.log('starting flood from', oldColor, this.frames[oldColor], 'to', newColor, this.frames[newColor]);
if (oldColor !== newColor) { if (oldColor !== newColor) {
@ -400,11 +259,6 @@ class PlayScene extends Phaser.Scene {
this.matched = []; this.matched = [];
if (this.monsterTween) {
this.monsterTween.stop(0);
}
this.cursor.setVisible(false);
this.instructions.setVisible(false); this.instructions.setVisible(false);
this.moves--; this.moves--;
@ -433,7 +287,7 @@ class PlayScene extends Phaser.Scene {
} }
startFlow() { startFlow() {
this.matched.sort(function(a, b) {
this.matched.sort(function (a, b) {
var aDistance = Phaser.Math.Distance.Between(a.x, a.y, 166, 66); var aDistance = Phaser.Math.Distance.Between(a.x, a.y, 166, 66);
var bDistance = Phaser.Math.Distance.Between(b.x, b.y, 166, 66); var bDistance = Phaser.Math.Distance.Between(b.x, b.y, 166, 66);
@ -457,9 +311,8 @@ class PlayScene extends Phaser.Scene {
this.time.delayedCall( this.time.delayedCall(
t, t,
function(block, blockColor) {
function (block, blockColor) {
block.setFrame(blockColor); block.setFrame(blockColor);
emitter.explode(6, block.x, block.y); emitter.explode(6, block.x, block.y);
}, },
[block, blockColor, emitter] [block, blockColor, emitter]
@ -470,7 +323,7 @@ class PlayScene extends Phaser.Scene {
this.time.delayedCall( this.time.delayedCall(
t, t,
function() {
function () {
this.allowClick = true; this.allowClick = true;
if (this.checkWon()) { if (this.checkWon()) {
@ -501,28 +354,6 @@ class PlayScene extends Phaser.Scene {
clearGrid() { clearGrid() {
// Hide everything :) // Hide everything :)
this.tweens.add({
targets: [
this.icon1.monster,
this.icon1.shadow,
this.icon2.monster,
this.icon2.shadow,
this.icon3.monster,
this.icon3.shadow,
this.icon4.monster,
this.icon4.shadow,
this.icon5.monster,
this.icon5.shadow,
this.icon6.monster,
this.icon6.shadow,
this.arrow,
this.cursor,
],
alpha: 0,
duration: 500,
delay: 500,
});
var i = 500; var i = 500;
for (var y = 13; y >= 0; y--) { for (var y = 13; y >= 0; y--) {
@ -564,7 +395,7 @@ class PlayScene extends Phaser.Scene {
duration: 1000, duration: 1000,
delay: i, delay: i,
}); });
this.game.onLose && this.game.onLose();
this.input.once("pointerdown", this.resetGame, this); this.input.once("pointerdown", this.resetGame, this);
} }
@ -575,30 +406,6 @@ class PlayScene extends Phaser.Scene {
// Show everything :) // Show everything :)
this.arrow.setFrame("arrow-white");
this.tweens.add({
targets: [
this.icon1.monster,
this.icon1.shadow,
this.icon2.monster,
this.icon2.shadow,
this.icon3.monster,
this.icon3.shadow,
this.icon4.monster,
this.icon4.shadow,
this.icon5.monster,
this.icon5.shadow,
this.icon6.monster,
this.icon6.shadow,
this.arrow,
this.cursor,
],
alpha: 1,
duration: 500,
delay: 500,
});
var i = 500; var i = 500;
for (var y = 13; y >= 0; y--) { for (var y = 13; y >= 0; y--) {
@ -639,11 +446,11 @@ class PlayScene extends Phaser.Scene {
this.currentColor = this.grid[0][0].getData("color"); this.currentColor = this.grid[0][0].getData("color");
var movesTween = this.tweens.addCounter({
this.tweens.addCounter({
from: 0, from: 0,
to: 25, to: 25,
ease: "Power1", ease: "Power1",
onUpdate: function(tween, targets, text) {
onUpdate: function (tween, targets, text) {
text.setText( text.setText(
Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1) Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1)
); );
@ -658,6 +465,7 @@ class PlayScene extends Phaser.Scene {
} }
gameWon() { gameWon() {
this.game.onWon && this.game.onWon();
this.stopInputEvents(); this.stopInputEvents();
this.text1.setText("Won!!"); this.text1.setText("Won!!");
@ -665,26 +473,6 @@ class PlayScene extends Phaser.Scene {
var i = this.clearGrid(); var i = this.clearGrid();
// Put the winning monster in the middle
var monster = this.add.image(
400,
300,
"flood",
"icon-" + this.frames[this.currentColor]
);
monster.setScale(0);
this.tweens.add({
targets: monster,
scaleX: 4,
scaleY: 4,
angle: 360 * 4,
duration: 1000,
delay: i,
});
this.time.delayedCall(2000, this.boom, [], this); this.time.delayedCall(2000, this.boom, [], this);
} }

BIN
games/game-flood-fill/scenes/bg.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 KiB

BIN
games/game-flood-fill/scenes/bu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
games/game-flood-fill/scenes/gr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
games/game-flood-fill/scenes/grey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
games/game-flood-fill/scenes/instruction.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
games/game-flood-fill/scenes/pr.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
games/game-flood-fill/scenes/red.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

146
games/game-flood-fill/scenes/tiles.json

@ -0,0 +1,146 @@
{
"textures": [
{
"image": "tiles.png",
"format": "RGBA8888",
"size": {
"w": 588,
"h": 98
},
"scale": 1,
"frames": [
{
"filename": "bu.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 1,
"y": 1,
"w": 96,
"h": 96
}
},
{
"filename": "gr.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 99,
"y": 1,
"w": 96,
"h": 96
}
},
{
"filename": "grey.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 197,
"y": 1,
"w": 96,
"h": 96
}
},
{
"filename": "pr.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 295,
"y": 1,
"w": 96,
"h": 96
}
},
{
"filename": "red.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 393,
"y": 1,
"w": 96,
"h": 96
}
},
{
"filename": "ye.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 96,
"h": 96
},
"frame": {
"x": 491,
"y": 1,
"w": 96,
"h": 96
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:448d6f6ac325281bdfac54338d3b71aa:d38095e268c28c803ed294a284637824:accbe1e7e294ded8391337fc1c446319$"
}
}

BIN
games/game-flood-fill/scenes/tiles.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
games/game-flood-fill/scenes/title.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
games/game-flood-fill/scenes/ye.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Loading…
Cancel
Save