ActivePointer location not updating - javascript

I'm making a sidescroller using Phaser (latest version) and I want the player's projectiles to go towards the pointer when the player clicks, like they do in this example http://phaser.io/examples/v2/games/tanks. I've used some of the code from the example but in my game, the activePointer x and y co-ordinates seem to only initialise when the game starts and never change. So when the player shoots, it's always going towards the same co-ordinates.
I have the following code (note I have removed bits about item collection, enemies etc. for posting on here):
var SideScroller = SideScroller || {};
var startPosX = 100;
var startPosY = 300;
var shooter;
var playerBullets;
var nextFire = 0;
var fireRate = 100;
var cursors;
var currentLoc;
SideScroller.Game = function () {};
SideScroller.Game.prototype = {
create: function () {
//create player
//params = (game, startPositionX,startPositionY, key, frame)
this.player = this.game.add.sprite(startPosX, startPosY, 'player');
//get canvas width and height for later use
canvasWidth = this.game.canvas.width;
canvasHeight = this.game.canvas.height;
//create enemy
var x = this.game.rnd.between(80, this.game.world.width);
var y = this.game.rnd.between(0, 113);
// Point to shoot projectiles from
// allows rotation, if this had been done on the player object, the graphic would have rotated, which we don't want
this.shooter = this.game.add.sprite(startPosX, startPosY, 'blank');
this.shooter.anchor.setTo(0.5, 0.5);
//make a group of player projectiles
playerBullets = this.game.add.group();
playerBullets.enableBody = true;
playerBullets.physicsBodyType = Phaser.Physics.ARCADE;
playerBullets.createMultiple(1000, 'peePower');
playerBullets.setAll('anchor.x', 0.5);
playerBullets.setAll('anchor.y', 0.5);
playerBullets.setAll('outOfBoundsKill', true);
playerBullets.setAll('checkWorldBounds', true);
//enable physics on the player
this.game.physics.arcade.enable(this.player);
//bring player shooting point to the top (not totally necessary)
this.shooter.bringToTop();
//player gravity
this.player.body.gravity.y = gravity;
//player collides with all four edges of the game world
this.player.body.collideWorldBounds = true;
this.player.anchor.setTo(0.5, 0.5);
//the camera will follow the player in the world
this.game.camera.follow(this.player);
//move player with cursor keys
cursors = this.game.input.keyboard.createCursorKeys();
},
update: function () {
currentLoc = this.game.input.activePointer;
//collision between player and platforms
this.game.physics.arcade.collide(this.player, this.blockedLayer, null, null, this);
//make co-ordinates match
this.shooter.x = this.player.x;
this.shooter.y = this.player.y;
//this.shooter's angle towards
this.shooter.rotation = this.game.physics.arcade.angleToPointer(this.shooter, this.game.input.activePointer);
//only respond to keys if the player is alive
if (this.player.alive) {
this.player.body.velocity.x = 0;
if (this.game.input.activePointer.isDown) {
console.log("pointer is down");
this.fire();
}
else if (cursors.right.isDown) {
this.playerForward();
}
else if (cursors.left.isDown) {
this.playerBack();
}
else if (cursors.up.isDown) {
this.playerJump();
}
else if (cursors.down.isDown) {
this.fire();
this.playerDuck();
}
}
},
fire: function () {
//for debugging
console.log("fire was called");
console.log(this.game.input.activePointer.x);
console.log(this.game.input.activePointer.y);
if (this.game.time.now > nextFire && playerBullets.countDead() > 0)
{
nextFire = this.game.time.now + fireRate;
var bullet = playerBullets.getFirstExists(false);
bullet.reset(this.shooter.x, this.shooter.y);
currentLoc = this.game.input.activePointer;
bullet.rotation = this.game.physics.arcade.moveToPointer(bullet, 1000, currentLoc, 1000);
console.log(this.game.input.activePointer);
}
},
playerForward: function () {
this.player.loadTexture('player');
this.player.body.setSize(this.player.standDimensions.width, this.player.standDimensions.height);
this.player.body.velocity.x = 700;
this.player.isMoving = true;
//console.log("Forward height:" + this.player.standDimensions.height);
//console.log("Forward width:" + this.player.standDimensions.width);
},
playerBack: function () {
this.player.loadTexture('playerBack');
this.player.body.velocity.x -= 700;
this.player.isMoving = true;
},
playerJump: function () {
if (this.player.body.blocked.down) {
this.player.body.velocity.y -= 700;
this.player.loadTexture('playerJump');
//console.log("Jump height:" + this.player.jumpDimensions.height);
//console.log("Jump width:" + this.player.jumpDimensions.width);
}
},
playerDuck: function () {
//change image and update the body size for the physics engine
this.player.loadTexture('playerDuck');
this.player.body.setSize(this.player.duckedDimensions.width, this.player.duckedDimensions.height);
//keep track of whether player is ducked or not
this.player.isDucked = true;
},
playerDead: function () {
//set to dead (this doesn't affect rendering)
this.player.alive = false;
//stop moving to the right
this.player.body.velocity.x = 0;
//change sprite image
this.player.loadTexture('playerDead');
},
};
Shooter is a blank sprite on top of the player (much like the turret in the tank example) to allow for rotation without the player rotating (please let me know also if there's a better way to do that!).
I tried updating the currentLoc variable in the update method to the activePointer location but that didn't work.
In addition, this condition has never been hit:
if (this.game.input.activePointer.isDown) {
console.log("pointer is down");
this.fire();
}
So something must be going awry with detecting mouse clicks and I don't know if that's part of the problem?

I think you should look it up in the API. There are few points in your code that are questionable.
http://phaser.io/docs/2.3.0/Phaser.Pointer.html
http://phaser.io/docs/2.3.0/Phaser.Physics.Arcade.html#moveToPointer
The point is that you are actually giving the reference to the pointer (to currentLoc) but not the position. So it should always fire to 0;0.
And for the isDown detection, have you done it in the update function or somewhere else?
Hope i could help!

Related

html canvas element blinking and then bugging out

I'm trying to have asteroids moving across the screen for a game. The first few asteroids work and then each asteroid will start blinking and bugging out to the point where they won't move across the screen. The variables acx and acy are the x and y coordinates for the asteroids respectively.
setInterval(throwAsteroid1A, 5000);
function throwAsteroid1A() {
var asteroidCanvas = document.getElementById('asteroidCanvas');
var context = asteroidCanvas.getContext('2d');
var acx = Math.floor(Math.random() * 200);
var acy = Math.floor(Math.random() * 10);
setInterval( () => {
asteroid.onload = function() {
context.drawImage(asteroid, asx, asy, aswidth, asheight, acx, acy, 20, 20);
acx += 1;
acy += 1;
}
asteroid.src = 'https://i.imgur.com/WfQKE6T.png';
}, 10)
setInterval(asteroidPath, 50)
}
function asteroidPath() {
// let computedStyle = getComputedStyle(canvasDisplay)
var asteroidCanvas = document.getElementById('asteroidCanvas');
let ctx = asteroidCanvas.getContext("2d");
ctx.clearRect(acx,acy, canvasDisplay.width, canvasDisplay.height);
}
Well there's obviously something conceptually wrong with your approach. I think the blinking is caused by a timing issue in-between the numerous individual interval timers you set up. The callback function asteroidPath() clears a part of the canvas and this might happen at the same time a new Asteroid has been added to the screen - which will delete it either entirely or partly depending on it's screen position.
To work around it you should:
keep a list of all asteroid objects
clear the screen completely once
update all asteroid's at once - not each one with it's own timer
So an example based on your code might look a little something like this (just click 'Run code snippet'):
Asteroid = function() {
this.acx = Math.floor(Math.random() * 200);
this.acy = Math.floor(Math.random() * 10);
this.image = new Image();
this.image.onload = function(e) {
this.loaded = true;
this.aswidth = e.target.naturalWidth;
this.asheight = e.target.naturalHeight;
}
this.image.src = 'https://i.imgur.com/WfQKE6T.png';
}
var asteroidCanvas = document.getElementById('asteroidCanvas');
var context = asteroidCanvas.getContext('2d');
let asteroids = [];
function spawnAsteroid() {
asteroids.push(new Asteroid());
}
function updateCanvas() {
context.clearRect(0, 0, asteroidCanvas.width, asteroidCanvas.height);
let asteroid;
for (let a = 0; a < asteroids.length; a++) {
asteroid = asteroids[a];
if (asteroid.image.loaded) {
context.drawImage(asteroid.image, 0, 0, asteroid.image.aswidth, asteroid.image.asheight, asteroid.acx, asteroid.acy, 20, 20);
asteroid.acx += 1;
asteroid.acy += 1;
}
}
}
setInterval(spawnAsteroid, 2000);
setInterval(updateCanvas, 50);
spawnAsteroid();
<canvas id="asteroidCanvas"></canvas>

JavaScript game switchable sprites

Any ideas how to make switchable characters I have a html game it's finished but I want to implement a way to switch my main character.
Simple coding using Phaser framework
upload function() {
this.game.load.sprite ("bird" assets/bird.png);
this.game.load.sprite ("bird2" assets/bird2.png);
this.game.load.sprite ("bird3" assets/bird3.png);
},
create function() {
this.game.add.sprite (0, 0 "bird" );
},
I want to be able to switch my playable character the "bird" with the "bird2" or "bird3" through a selection button if a player selects a switch character button for the playable character to switch to that. I'm pretty sure this is something simple but I'm still pretty new with coding.
I want a button where I press then I can switch the character
(Button 1) switches to bird2
"if button 1 is selected button two and current bird are disabled"-only bird2 is visible
(Button 2) switches to bird3
"if button 2 is selected button one and current bird are disabled"-only bird3 is visible
Edit This is My current code and states
var MainState = {
//load the game assets before the game starts
preload: function () {
this.load.image('background', 'assets/spring2.png');
this.load.spritesheet('bird', 'assets/bird.png',52 ,28, 7);
this.load.spritesheet('bird2', 'assets/bird2.png',52 ,28, 7);
this.load.spritesheet('bird3', 'assets/bird3.png',52 ,28, 7);
this.load.image('pipe', 'assets/pipe4.png');
},
//executed after everything is loaded
create: function () {
this.background = game.add.tileSprite(0, game.height-736,game.width, 736, 'background');
this.background.autoScroll(-20,0);
/////Bird///////////////////////////////////////////////////
this.bird = this.game.add.sprite(100, 200, 'bird');
this.bird.animations.add('fly');
this.bird.animations.play('fly', 50, true);
game.physics.startSystem(Phaser.Physics.ARCADE);
game.physics.arcade.enable(this.bird);
this.bird.body.gravity.y = 1000;
var spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
this.bird.body.collideWorldBounds=true;
this.bird.body.immovable= true;
game.input.onDown.add(this.jump, this); //////touch screen jump
spaceKey.onDown.add(this.jump, this);
///////////////////////////////////////////////////////Pipes
this.pipes = game.add.group();
//timer
this.timer = game.time.events.loop(1600, this.addRowOfPipes, this); /////////////timer for pipes
///Bird anchor
this.bird.anchor.setTo(-0.2, 0.5)
},
// this is execated multiple times per second
update: function () {
if (this.bird.y < 0 || this.bird.y > 480)
game.state.start("StateOver");
///Collision
game.physics.arcade.overlap(
this.bird, this.pipes, this.restartGame, null, this);
///Bird Angle
if (this.bird.angle < 30)
this.bird.angle += 1;
///////////////music stop w top+bottom collision
if (this.bird.y < 0 || this.bird.y > 479)
music.stop();
},
jump: function () {
//this is for so the bird wount fly once dead
if (this.bird.alive == false)
return;
// Add a vertical velocity to the bird
this.bird.body.velocity.y = -350;
// Jump Animation
var animation = game.add.tween(this.bird);
// Change the angle of the bird to -20° in 100 milliseconds
animation.to({angle: -20}, 100);
// And start the animation
animation.start();
game.add.tween(this.bird).to({angle: -20}, 100).start();
},
restartGame: function () {
// Start the 'main' state, which restarts the game
game.state.start(game.state.StateOver); /////////////////////changed from current #########
///Hit pipe Null
game.physics.arcade.overlap(
this.bird, this.pipes, this.hitPipe, null, this);
},
addRowOfPipes: function() {
var hole = Math.floor(Math.random() * 5) + 1; ///Math.floor(Math.random() * 5) + 1;
for (var i = 0; i < 10 ; i++) ///// (var i = 0; i < 8; i++)
if (i != hole && i != hole + 1) ///// if (i != hole && i != hole + 1)
this.addOnePipe(440, i * 50 ); ///// 640 starting point of pipe 240 point of down ////this.addOnePipe(480, i * 60 + 10);
},
addOnePipe: function(x, y) {
var pipe = game.add.sprite(x, y, 'pipe');
this.pipes.add(pipe);
game.physics.arcade.enable(pipe);
pipe.body.velocity.x = -200;
pipe.checkWorldBounds = true;
pipe.outOfBoundsKill = true;
},
hitPipe: function() {
// If the bird has already hit a pipe, do nothing
// It means the bird is already falling off the screen
if (this.bird.alive == false)
return;
else {
game.state.start("StateOver");
}
// Set the alive property of the bird to false
this.bird.alive = false;
// Prevent new pipes from appearing
game.time.events.remove(this.timer);
// Go through all the pipes, and stop their movement
this.pipes.forEach(function(p){
p.body.velocity.x = 0;
}, this);
},
};
character.js
var characters={
preload:function()
{
game.load.spritesheet('button', 'assets/button.png', 215, 53, 8);
game.load.image("background", "assets/characterbackground.png");
game.load.image("pajaro", "assets/storeicon.png");
game.load.image("logo", "assets/extra/storef.png");
this.load.spritesheet('bird', 'assets/bird.png',52 ,28, 7);
this.load.spritesheet('bird2', 'assets/bird2.png',52 ,28, 7);
this.load.spritesheet('bird3', 'assets/bird3.png',52 ,28, 7);
game.load.spritesheet("button2", 'assets/button2.png', 100, 10, 10);
},
create:function()
{
bird = game.add.image(140, 150, 'pajaro');
logo = game.add.image (20, 350, 'logo');
this.background = game.add.tileSprite(0, game.height-736,game.width, 736, 'background');
this.background.autoScroll(-100,0);
this.btnMainMenu=game.add.button(130,500,'button',this.mainMenu,this,4,5,4);
this.btnbird=game.add.button(180,600,"button2",this.changebird2,this,0,1,0);
},
mainMenu:function()
{
game.state.start("stateTitle");
},
update:function()
{
// bird.x +=1;
},
changebird2: function(){
},
};
Instead of creating three sprites that you either hide or show, I might recommend just changing what texture is loaded when the sprite is created/added.
To do this you'll need to store a reference to the playable character, which you probably already have.
// On the game itself, add a reference.
this.bird = null;
// In your preload, load the different images.
this.load.image('bird', 'assets/bird.png');
this.load.image('bird2', 'assets/bird2.png');
this.load.image('bird3', 'assets/bird3.png');
// When creating, default to one.
this.bird = this.game.add.sprite(0, 0, 'bird');
// In your function where they select a new skin, you can load in a different texture.
this.bird.loadTexture('bird3');
Alternatively, you could store the key that should be used on the game.
// On the game itself, track which key to use.
this.birdSkin = 'bird';
// You'll still have to load your possible textures.
this.load.image('bird', 'assets/bird.png');
this.load.image('bird2', 'assets/bird2.png');
this.load.image('bird3', 'assets/bird3.png');
// Now when creating just use the variable.
this.bird.loadTexture(this.birdSkin);
The Phaser init() will allow 0 or more parameters to be passed in (see the end of Phaser Tutorial: understanding Phaser states), which is where you could populate this.birdSkin.
I would look at what states you're using to determine what's best for you. If you have one state for the game and another for selecting which image/texture is used, than the second option might be better.
Update for Character State
Given your comments and what I saw in your code, I created a short example that you could tweak for your use.
There's a JSFiddle available, but the code is also included below.
var mainState = {
preload: function() {
// Load the three sprites that they can choose between.
this.load.crossOrigin = 'anonymous';
this.load.image('ball', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-blue.png');
this.load.image('ball2', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-green.png');
this.load.image('ball3', 'https://raw.githubusercontent.com/photonstorm/phaser-examples/master/examples/assets/sprites/orb-red.png');
},
create: function() {
this.ball = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, this.game.global.skin);
this.ball.anchor.setTo(0.5);
// Let the ball be acted upon. This will allow the player to change the sprite used.
this.ball.inputEnabled = true;
this.ball.events.onInputDown.add(this.changeCharacter, this);
},
update: function() {
},
changeCharacter: function() {
game.state.start('character');
}
};
var characterState = {
preload: function() {
},
create: function() {
// For this, add our three possible ball skins.
this.ball1 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY / 2, 'ball');
this.ball1.anchor.setTo(0.5);
this.ball1.inputEnabled = true;
this.ball2 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY, 'ball2');
this.ball2.anchor.setTo(0.5);
this.ball2.inputEnabled = true;
this.ball3 = this.game.add.sprite(this.game.world.centerX, this.game.world.centerY * 1.5, 'ball3');
this.ball3.anchor.setTo(0.5);
this.ball3.inputEnabled = true;
// Use the selected ball's sprite in our main game.
this.ball1.events.onInputDown.add(this.selectBall, this);
this.ball2.events.onInputDown.add(this.selectBall, this);
this.ball3.events.onInputDown.add(this.selectBall, this);
},
update: function() {
},
selectBall: function(sprite, pointer) {
// Grab the key of the sprite and save it to our global variable.
this.game.global.skin = sprite.key;
this.game.state.start('main');
}
};
var game = new Phaser.Game(200, 200);
// Create a global object that we can add custom variables to.
game.global = {
skin: 'ball'
};
game.state.add('main', mainState);
game.state.add('character', characterState);
game.state.start('main');
This actually simplifies things a bit, in that it just uses a global variable (I've been using TypeScript the last handful of months, so there's probably a better way to declare this).

How to create objects on runtime and move them?

I'm trying to create objects on my game update and move them. This is my banana object:
function Banana() {
this.height = 1.96;
this.width = 3.955;
this.pos_x = CENTER - this.width/2;
this.pos_y = -475;
this.banana_image = banana_image;
};
And this is the Move method:
Banana.prototype.move = function(){
if (this.pos_y > 500) {
//this.banana_image.parentElement.removeChild(this.banana_image);
}
this.height += ZOOM_RATE;
this.width += ZOOM_RATE;
this.pos_y += 3;
this.pos_x -= SIDES_RATE;
};
This is the Game Update part:
Game.update = function() {
this.player.move();
//creating bananas
if (objs.lenght <= 0) {
this.banana = new Banana();
} else {
for (i = 0; i < 10; i++) {
objs.push(new Banana());
}
}
//moving bananas
for (i = 0; i < objs.lenght; i++) {
this.objs[0].move();
}
};
Game Draw:
function Game.draw = function() {
this.context.drawImage(road, 0,0, rw, rh);
this.context.drawImage(
this.player.player_image,
this.player.pos_x, this.player.pos_y,
this.player.width, this.player.height);
this.context.drawImage(
this.banana.banana_image,
this.banana.pos_x, this.banana.pos_y,
this.banana.width, this.banana.height);
};
I tried to ask this to multiple people, but I can't find an answer for it.
Let's say you want to move the objects 10 times and then stop.
First you need to add a line to the start of Game.draw, so that it clears the canvas making you always start drawing from scratch:
this.context.clearRect(0,0,500,500); // clear canvas, adjust box size if needed
Then make a function to call both update and draw, and queue that function to be called again:
var count = 10;
function updateAndDraw() {
Game.update();
Game.draw();
count--;
if (count) requestAnimationFrame(updateAndDraw);
}
// start moving:
requestAnimationFrame(updateAndDraw);
The movement may go too fast to your liking, so then adjust the move method to make smaller changes, or use setTimeout instead of requestAnimationFrame (but that will make the animation less fluent).
Note that you have a few errors in your code, which you will need to fix first:
lenght should be length
function Game.draw = function() {: remove function before Game.draw.
... check the error messages you get in console.

SVG Camera Pan - Translate keeps resetting to 0 evertime?

Well i have this SVG canvas element, i've got to the point so far that once a user clicks and drags the canvas is moved about and off-screen elements become on screen etc....
However i have this is issue in which when ever the user then goes and click and drags again then the translate co-ords reset to 0, which makes the canvas jump back to 0,0.
Here is the code that i've Got for those of you whio don't wanna use JS fiddle
Here is the JSfiddle demo - https://jsfiddle.net/2cu2jvbp/2/
edit: Got the solution - here is a JSfiddle DEMO https://jsfiddle.net/hsqnzh5w/
Any and all sugesstion will really help.
var states = '', stateOrigin;
var root = document.getElementById("svgCanvas");
var viewport = root.getElementById("viewport");
var storeCo =[];
function setAttributes(element, attribute)
{
for(var n in attribute) //rool through all attributes that have been created.
{
element.setAttributeNS(null, n, attribute[n]);
}
}
function setupEventHandlers() //self explanatory;
{
setAttributes(root, {
"onmousedown": "mouseDown(evt)", //do function
"onmouseup": "mouseUp(evt)",
"onmousemove": "mouseMove(evt)",
});
}
setupEventHandlers();
function setTranslate(element, x,y,scale) {
var m = "translate(" + x + "," + y+")"+ "scale"+"("+scale+")";
element.setAttribute("transform", m);
}
function getMousePoint(evt) { //this creates an SVG point object with the co-ords of where the mouse has been clicked.
var points = root.createSVGPoint();
points.x = evt.clientX;
points.Y = evt.clientY;
return points;
}
function mouseDown(evt)
{
var value;
if(evt.target == root || viewport)
{
states = "pan";
stateOrigin = getMousePoint(evt);
console.log(value);
}
}
function mouseMove(evt)
{
var pointsLive = getMousePoint(evt);
if(states == "pan")
{
setTranslate(viewport,pointsLive.x - stateOrigin.x, pointsLive.Y - stateOrigin.Y, 1.0); //is this re-intializing every turn?
storeCo[0] = pointsLive.x - stateOrigin.x
storeCo[1] = pointsLive.Y - stateOrigin.Y;
}
else if(states == "store")
{
setTranslate(viewport,storeCo[0],storeCo[1],1); // store the co-ords!!!
stateOrigin = pointsLive; //replaces the old stateOrigin with the new state
states = "stop";
}
}
function mouseUp(evt)
{
if(states == "pan")
{
states = "store";
if(states == "stop")
{
states ='';
}
}
}
In your mousedown function, you are not accounting for the fact that the element might already have a transform and you are just overwriting it.
You are going to need to either look for, and parse, any existing transform. Or an easier approach would be to keep a record of the old x and y offsets and when a new mousedown happens add them to the new offset.

html5 canvas code blanking out

I am in desperate need to fix a problem I have in html5 canvas. I am making an educational game, but after adding another photo and making the necessary code changes, but the canvas blanked out. I reverted the changes, but the problem persisted. I spent the past week or so trying to fix it, but I've run out of ideas :( Here is the relevent code (if anyone needs another part, just say so):
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 1041;
canvas.height = 550;
document.body.appendChild(canvas);
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "images/introbackground.png";
//.... rest of the photos load like this so I won't put it...
// Level 1 background 1 image
var bbg1Ready = false;
var bbg1Image = new Image();
bbg1Image.onload = function () {
bbg1Ready = false; //Exception since it shouldn't load at the beginning
};
bbg1Image.src = "images/pollutedbeach1.png";
// Game objects
var hero = {
speed: 200 // movement in pixels per second
};
var level1;
var level2;
var biolevel = false;
//...Code to make the hero picture move with arrow keys and to reset the game are skipped...
if (hero.x <= (level1.x + 345)
&& level1.x <= (hero.x + 32)
&& hero.y <= (level1.y +50)
&& level1.y <= (hero.y + 32)){
biolevel = true;
return biolevel;
}
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
//...The same for all photos previously loaded...
if (biolevel == true){
level1Ready = false;
level2Ready = false;
bbg1Ready = true;
hero.speed = 125;
}
};
//...Then the main game loop to animate it...
Sorry It's kind of long, but I really need a solution. Thanks!

Categories

Resources